问题描述
我正在尝试创建一个简单的异常类,但是遇到了这个烦人的问题,不知道出了什么问题。请注意,我是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
类成员。由于这是一个常量类方法,因此无法做到这一点。
显示的代码中还有三个其他基本错误:
-
两次调用
what()
将泄漏内存。 -
由于构造函数从未将
msg
初始化为任何东西,而析构函数delete
对其进行了初始化,因此 不是 调用{{1} }将导致内存损坏,并可能导致崩溃。而且由于没有复制构造函数,因此复制对象有时会导致双what()
,并导致另一次崩溃。 -
所示的代码
delete
是new
数组,用于存储消息,但从不实际将其设置为任何内容。返回的char
()字符串将是完整的垃圾邮件。
您必须解决所有这些问题,才能使自定义异常对象正常工作。
解决所有内存损坏的最简单方法就是根本不手动进行内存分配。
请注意,我是C ++新手(因此尝试学习如何使用new 而不是malloc。
现代C ++代码很少需要what
或new
任何东西。当然,了解内存分配是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");