C++ private char* 成员分配失败 'Expression must be a modifiable lvalue'

C++ private char* member allocation failure 'Expression must be a modifiable lvalue'

我正在尝试创建一个简单的异常 class 但我遇到了这个烦人的问题并且不知道出了什么问题。请注意,我是 C++ 菜鸟(因此尝试学习如何使用 new 而不是 malloc)。 这是代码:

#include <errno.h>
#include <exception>
#include <string.h>

/**
 * An exception describing a failure in creating a socket.
 */
class SocketCreationException : public std::exception
{
private:
    char *msg;

public:
    ~SocketCreationException()
    {
        delete msg;
    }

    const char *what() const throw()
    {
        const char *str = "Socket creation failure:";
        char *err = strerror(errno);
        msg = new char[strlen(str) + strlen(err) + 1]; // Error here
        return msg;
    }
};

编辑: 很多很棒的答案和提示!谢谢大家的帮助!真的很有用!

成员函数是 const 限定的。因此它不能更改成员的值。

实际上,异常应该在它们的构造函数中初始化它们的消息,并且只有 return 指向 what.

中已经构造的消息的指针

此外,errno 可能已被某些操作覆盖。您应该将 throw 表达式中的值作为参数传递给异常的构造函数。

此外,class 是可复制的,但在复制时会导致未定义的行为。

此外,如果您的程序编译后,what 被多次调用,那么它会泄漏内存。此外,您永远不会在分配的内存中存储任何内容。

您可能应该继承自 std::runtime_error,它有一个接受消息的构造函数,这样您就不需要自己实现存储消息,这在异常情况下实际上非常棘手。

I am C++ noob (hence trying to learn how to use new

这不是您应该使用 new 的用例(事实上,此类用例极为罕见)。如果 std::runtime_error 继承不是一个更好的选择,您应该使用 std::string(您不应该直接使用它但有异常,因为复制可能会抛出异常,但在其他情况下它很好)。

这是一个正确的例子:

class SocketCreationException : public std::runtime_error
{
public:
    SocketCreationException(int number)
        : std::runtime_error(message(number));
    {}

private:
    std::string message(int number)
    {
        return "Socket creation failure: "s
             + std::strerror(number);
    }
};

// usage (immediately after operation that set errno)
int number = errno;
if (number)
    throw SocketCreationException(errno);

编辑:我忘记了 std::system_error 这恰好适用于此用例,请参阅 答案。

const char *what() const throw()

这是一个常量class方法,这就是const关键字的意思。

msg = new char[strlen(str) + strlen(err) + 1]; // Error here

这试图修改 msg class 成员。由于这是一个常量 class 方法,它不能这样做。

显示的代码中还有三个基本错误:

  1. 调用what()两次会泄漏内存。

  2. 由于构造函数从不将 msg 初始化为任何东西,而析构函数 delete 初始化它, 调用 what() 将导致内存损坏,并可能导致崩溃。而且由于没有复制构造函数,复制对象会在某些时候导致双重 delete 和另一个崩溃。

  3. 显示的代码 newchar 数组,用于存储消息,但实际上从未将其设置为任何内容。返回的 what() 字符串将是完整的并且完全是垃圾。

您必须解决所有这些问题,您的自定义异常对象才能正常工作。

解决所有内存损坏的最简单方法就是从一开始就不要进行手动内存分配。

Please note that I am C++ noob (hence trying to learn how to use new instead of malloc).

现代 C++ 代码很少需要 newdelete 任何东西。当然,了解内存分配是 C++ 的基础,但很少需要实际使用它。在这种情况下,std::string 会完成此处所需的一切。因此,请改用 std::string。不需要析构函数。无需自己获得 100% 正确的内存分配。您的 std::string 会为您完成。

并且要能够在constclass方法中修改class方法,只需声明为mutable.

mutable std::string msg;

// ...

msg="Socket creation failure: ";
msg += strerror(errno);
return msg.c_str();

这比必须计算和跟踪每个人 char 并正确实施内存分配逻辑要容易得多。

除了其他答案中所说的以外,实际上没有必要在此处声明自定义异常 class。 std::system_error已经可以为所欲为了。例如你可以这样做:

throw std::system_error(errno, std::generic_category(), "Socket creation failure");