我从 std::system_error
派生我自己的异常,将其称为 MyException
并覆盖了 what()
来计算和回我的消息。 MyException
的初始化列表不会调用接收消息的 system_error 构造函数覆盖。
如果我捕获一个 MyException
并将其复制到一个 std::exception
调用 上的
是 what()
的结果std::exceptionnullptr
。这是有道理的。
我的问题是,如果我确实使用了在初始化 MyException
时获取消息的 system_exception 构造函数,是否指定 system_error 将获取消息的拷贝并拥有它并释放它?
我假设这将使 MyException
的 std::exception
拷贝能够返回有效的 what()
。尽管每次创建新的 MyExceptions
时都需要计算“什么”,但我会对性能造成影响;我不能仅在第一次调用 what() 时懒惰地计算它。
我有点担心 'what' 字符串的所有权,因为 what()
返回 char*
而不是 const std::string&
。
代码是这样的(我还没有编译这个):
class MyException : public std::system_error
{
std::string what_;
public:
MyException(int errorValue, const std::error_category& category)
: std::system_error(errorValue, category)
{}
char* what() const
{
what_ = "MyException: " + to_string(code().value());
return what_.c_str();
}
};
int main()
{
std::exception ex;
try
{
throw MyException(4, system_category());
}
catch( const MyException& e )
{
ex = e;
}
printf("what= %s", ex.what());
return 1;
}
最佳答案
My question is, if I do use the constructor of system_exception that takes a message when initializing
MyException
, is it specified that system_error will take a copy of the message and own it and free it?
是的,这是由标准保证的。
开始,std::exception
不拥有what
– std::runtime_error
做。 std::runtime_error
的构造函数是这样定义的([runtime.error]p2-5):
runtime_error(const string& what_arg);
Effects: Constructs an object of class
runtime_error
.
Postcondition:strcmp(what(), what_arg.c_str()) == 0
.runtime_error(const char* what_arg);
Effects: Constructs an object of class
runtime_error
.
Postcondition:strcmp(what(), what_arg) == 0
.
因此,它必须存储 what_arg
的拷贝在内部,因为对传入值的生命周期没有要求。
接下来是[异常]p2:
Each standard library class
T
that derives from classexception
shall have a publicly accessible copy constructor and a publicly accessible copy assignment operator that do not exit with an exception. These member functions shall meet the following postcondition: If two objectslhs
andrhs
both have dynamic typeT
andlhs
is a copy ofrhs
, thenstrcmp(lhs.what(), rhs.what())
shall equal0
.
所以,必须有一个复制构造函数,它绝不能抛出,并且拷贝必须为what()
保持相同的返回值。 .复制赋值运算符也是如此。
将所有这些放在一起,我们可以推测 std::runtime_error
必须保留您为 what_arg
传递的值在引用计数的字符串内部(以避免复制时分配的异常),并且无论复制和/或切片如何,该值都将持续存在 - 但仅限于 std::runtime_error
,不到std::exception
! (有关 what
的存储的基本原理和要求的更多信息可以在 @HowardHinnant 的这个非常有趣的答案中找到:move constructor for std::runtime_error)
std::system_error
继承自 std::runtime_error
,因此对于它和从它派生的任何类型都同样适用(只要派生类型保持非抛出复制构造函数不变)。
I'm assuming this would enable a
std::exception
copy ofMyException
to be able to return a validwhat()
.
不!当你制作 std::exception
MyException
的拷贝 , 你是 slicing对象的派生类型低于 where what
的值是物理存储的。如果您必须复制您的异常,您可以使用的最少派生类型是std::runtime_error
。 . (当然,您始终可以安全地将 std::exception
reference 设为 MyException
。)换句话说,永远 不可能获得有意义的字符串来自std::exception
对象的what()
.
此代码具有您想要的可移植行为:
#include <cstdio>
#include <stdexcept>
#include <system_error>
#include <string>
class MyException : public std::system_error {
public:
MyException(int errorValue, std::error_category const& category)
: std::system_error(
errorValue, category,
"MyException: " + std::to_string(errorValue)
)
{ }
};
int main() {
std::runtime_error ex;
try {
throw MyException(4, system_category());
} catch(MyException const& e) {
ex = e;
}
std::printf("what= %s", ex.what());
}
我会说编写分配的异常构造函数是一种糟糕的形式(出于显而易见的原因),但考虑到我所知道的每个当前标准库实现都使用 short-string optimization对于 std::basic_string<>
,这在实践中极不可能成为问题。
关于c++ - std::exception 是否拥有 `what()` ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38479375/