C ++专用char *成员分配失败“表达式必须是可修改的左值”

问题描述

我正在尝试创建一个简单的异常类,但是遇到了这个烦人的问题,不知道出了什么问题。请注意,我是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限定的。因此,它不能更改成员的值。

在实践中,异常应在其构造函数中初始化其消息,并且仅返回指向what中已构造的消息的指针。

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

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

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

您可能应该从std::runtime_error继承,该new具有接受消息的构造函数,因此您无需自己实现存储消息,这在遇到异常的情况下实际上非常棘手。

我是C ++新手(因此尝试学习如何使用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

编辑:我忘了回合{{1}},这恰好适合此用例,请参见this答案。

,
const char *what() const throw()

这是一个常量类方法,这就是const关键字的含义。

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

这将尝试修改msg类成员。由于这是一个常量类方法,因此无法做到这一点。

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

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

  2. 由于构造函数从未将msg初始化为任何东西,而析构函数delete对其进行了初始化,因此 不是 调用{{1} }将导致内存损坏,并可能导致崩溃。而且由于没有复制构造函数,因此复制对象有时会导致双what(),并导致另一次崩溃。

  3. 所示的代码deletenew数组,用于存储消息,但从不实际将其设置为任何内容。返回的char()字符串将是完整的垃圾邮件。

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

解决所有内存损坏的最简单方法就是根本不手动进行内存分配。

请注意,我是C ++新手(因此尝试学习如何使用new 而不是malloc。

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

要能够在std::string类方法中修改类方法,只需将其声明为const

mutable

与计算和跟踪每个mutable std::string msg; // ... msg="Socket creation failure: "; msg += strerror(errno); return msg.c_str(); 并正确实现内存分配逻辑相比,这要容易得多。

,

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

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