我正在派生我自己的异常,调用它MyException
,std::system_error
从而重写what()
以计算并返回我的消息.MyException
的初始化列表不会调用带有消息的system_error构造函数覆盖.
如果我抓住一个MyException
,并将其复制到std::exception
调用的结果what()
上std::exception
就是nullptr
.这是有道理的.
我的问题是,如果我确实使用了在初始化时接收消息的system_exception的构造函数MyException
,是否指定system_error将获取消息的副本并拥有它并释放它?
我假设这将使std::exception
副本MyException
能够返回有效的what()
.虽然我会在每次MyExceptions
创建新内容时计算"什么"需要考虑性能.只有在首次调用what()时,我才能懒惰地计算它.
我有点担心'what'字符串的所有权what()
返回a char*
而不是a 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; }
ildjarn.. 7
我的问题是,如果我确实使用了在初始化时接收消息的system_exception的构造函数
MyException
,是否指定system_error将获取消息的副本并拥有它并释放它?
是的,这是由标准保证的.
首先,std::exception
不拥有what
- std::runtime_error
是的.std::runtime_error
因此定义了构造函数([runtime.error] p2-5):
runtime_error(const string& what_arg);效果:构造一个类的对象
runtime_error
.
后置条件:strcmp(what(), what_arg.c_str()) == 0
.runtime_error(const char* what_arg);效果:构造一个类的对象
runtime_error
.
后置条件:strcmp(what(), what_arg) == 0
.
因此,它必须保存一个拷贝的what_arg
内部,因为没有关于传入的值的寿命要求.
接下来是[例外] p2:
T
从类派生的每个标准库类exception
都应具有可公开访问的复制构造函数和不能以异常退出的可公开访问的复制赋值运算符.这些成员函数应满足以下后置条件:如果两个对象lhs
和rhs
两个具有动态型T
和lhs
是的副本rhs
,那么strcmp(lhs.what(), rhs.what())
应等于0
.
因此,必须有一个复制构造函数,它必须永远不会抛出,并且副本必须保持相同的返回值what()
.同样,对于复制赋值运算符.
把所有这些组合起来,我们可以推测,std::runtime_error
必须保留传入的值what_arg
在引用计数的字符串内部(避免分配例外复制时),该值将长期存在,无论复制和/或切片-但只下到std::runtime_error
,不要下来std::exception
!(关于what
存储的基本原理和要求的更多信息可以在@HowardHinnant的这个非常有趣的答案中找到:为std :: runtime_error移动构造函数)
std::system_error
继承自std::runtime_error
,所以同样适用于它以及从它派生的任何类型(只要派生类型保持非抛出复制构造函数不变).
我假设这将使
std::exception
副本MyException
能够返回有效的what()
.
没有!当你犯了一个std::exception
副本的MyException
,你是切片对象降到更低派生类型比这里what
的值的物理存储位置.如果必须复制异常,则可以使用的派生类型最少std::runtime_error
.(您可以随时安全地进行std::exception
参考的MyException
,当然).换一种方式,它是永远不会可能获得从一个有意义的字符串std::exception
对象的what()
.
此代码具有您想要的行为,可移植:
#include#include #include #include 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()); }
我会说,这是一种欠佳编写分配(原因很明显)异常的构造,但考虑到每一个现行标准库的实现,我所知道的用途短串优化了std::basic_string<>
,这是极不可能永远是一个问题在实践中.
我的问题是,如果我确实使用了在初始化时接收消息的system_exception的构造函数
MyException
,是否指定system_error将获取消息的副本并拥有它并释放它?
是的,这是由标准保证的.
首先,std::exception
不拥有what
- std::runtime_error
是的.std::runtime_error
因此定义了构造函数([runtime.error] p2-5):
runtime_error(const string& what_arg);效果:构造一个类的对象
runtime_error
.
后置条件:strcmp(what(), what_arg.c_str()) == 0
.runtime_error(const char* what_arg);效果:构造一个类的对象
runtime_error
.
后置条件:strcmp(what(), what_arg) == 0
.
因此,它必须保存一个拷贝的what_arg
内部,因为没有关于传入的值的寿命要求.
接下来是[例外] p2:
T
从类派生的每个标准库类exception
都应具有可公开访问的复制构造函数和不能以异常退出的可公开访问的复制赋值运算符.这些成员函数应满足以下后置条件:如果两个对象lhs
和rhs
两个具有动态型T
和lhs
是的副本rhs
,那么strcmp(lhs.what(), rhs.what())
应等于0
.
因此,必须有一个复制构造函数,它必须永远不会抛出,并且副本必须保持相同的返回值what()
.同样,对于复制赋值运算符.
把所有这些组合起来,我们可以推测,std::runtime_error
必须保留传入的值what_arg
在引用计数的字符串内部(避免分配例外复制时),该值将长期存在,无论复制和/或切片-但只下到std::runtime_error
,不要下来std::exception
!(关于what
存储的基本原理和要求的更多信息可以在@HowardHinnant的这个非常有趣的答案中找到:为std :: runtime_error移动构造函数)
std::system_error
继承自std::runtime_error
,所以同样适用于它以及从它派生的任何类型(只要派生类型保持非抛出复制构造函数不变).
我假设这将使
std::exception
副本MyException
能够返回有效的what()
.
没有!当你犯了一个std::exception
副本的MyException
,你是切片对象降到更低派生类型比这里what
的值的物理存储位置.如果必须复制异常,则可以使用的派生类型最少std::runtime_error
.(您可以随时安全地进行std::exception
参考的MyException
,当然).换一种方式,它是永远不会可能获得从一个有意义的字符串std::exception
对象的what()
.
此代码具有您想要的行为,可移植:
#include#include #include #include 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()); }
我会说,这是一种欠佳编写分配(原因很明显)异常的构造,但考虑到每一个现行标准库的实现,我所知道的用途短串优化了std::basic_string<>
,这是极不可能永远是一个问题在实践中.