我工作的工程里现在代码里有这样的使用方法:
class Exception : public std::exception
{
public:
Exception(const char * const msg)
:std::exception(msg)
{
}
};
void throwFunc()
{
std::string str("abc");
throw Exception(str.c_str());
}
void main()
{
try
{
throwFunc();
}
catch (const std::exception&e)
{
std::cout << e.what();
}
}
以这种方式使用实际上是没有问题的,但是总感觉有点怪怪的。
问题的核心在于
std::exception(char const* const _Message);
传入的参数是一个原始指针,这种指针并没有规定其指向的对象的生命周期由谁管理,通常是由调用者管理,也就是谁申请谁释放。
第一段代码的用法也是这样,但是上述构造函数有进行复制吗?
explicit exception(char const* const _Message) throw()
: _Data()
{
__std_exception_data _InitData = { _Message, true };
__std_exception_copy(&_InitData, &_Data);
}
看起来 __std_exception_copy函数是复制数据的,但是我没有查到相关文档,只能看实验的结论了。
当然结论是确实复制了, __std_exception_data的另一个成员名是_DoFree,看来就是明确字符串归属问题的。所以这个构造方法是可以保证资源的有效性,也能正确释放。
但是exception还有另外一个构造函数:
exception(char const* const _Message, int) throw()
: _Data()
{
_Data._What = _Message;
}
就多了一个匿名的int参数,看起来这个重载形式的意思就是只存储一下指针,并不管理也不关心资源的问题。
当我刚看到这个函数时感觉非常奇怪:这个函数的目的是啥?为了省掉复制这个字符串的时间?都抛异常了还在乎这点时间吗?
但是就在写这个文档时,突然想到复制字符串除了需要时间,还需要空间。如果当前这个异常本身就是bad_alloc,那可能连容纳这个字符串的地方都没有了,复制会失败。所以这个形式还是有意义的。
PS:请思考当内存耗尽时如何正常地处理类似异常、写日志等任务?
PS:C++标准中std::exception只有一个无参构造和复制构造。