通过引用捕获异常是否危险
Is catching an exception by reference dangerous?
请看一下以下异常抛出和捕获:
void some_function() {
// Was std::exception("message") in original post, which won't compile
throw std::runtime_error("some error message");
}
int main(int argc, char **argv) {
try {
some_function();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
exit(1);
}
return 0;
}
通过引用捕获引发的异常是否安全?
我担心的是,异常e
实际上放在some_function()
堆栈上。但some_function()
刚刚回来,导致e
被破坏。所以实际上现在e
指向一个被破坏的对象。
我的担忧正确吗?
传递异常而不按值复制它的正确方法是什么?我应该扔new std::exception()
以便将其放置在动态内存中吗?
const
参考捕获确实是安全的 - 并且建议这样做。
"
e
其实是放在some_function()
堆上">
不,不是...实际抛出的对象是在为异常处理机制保留使用的未指定内存区域中创建的:
[except.throw] 15.1/4:异常对象的内存以未指定的方式分配,但 3.7.4.1 中所述除外。 例外 在异常的最后一个剩余活动处理程序通过重新引发以外的任何方式退出或引用异常对象的最后一个 std::exception_ptr (18.8.5( 类型的对象被销毁后,对象将被销毁,以较晚者为准。
如果指定了一个局部变量来throw
,则在必要时将其复制到该变量(优化器可能能够直接在另一个内存中创建它(。 这就是为什么...
15.1/5 当抛出的对象是类对象时,为复制初始化和析构函数选择的构造函数 应可访问,即使省略了复制/移动操作 (12.8(。
如果没有点击,模糊地想象像这样实现可能会有所帮助:
// implementation support variable...
thread__local alignas(alignof(std::max_align_t))
char __exception_object[EXCEPTION_OBJECT_BUFFER_SIZE];
void some_function() {
// throw std::exception("some error message");
// IMPLEMENTATION PSEUDO-CODE:
auto&& thrown = std::exception("some error message");
// copy-initialise __exception_object...
new (&__exception_object) decltype(thrown){ thrown };
throw __type_of(thrown);
// as stack unwinds, _type_of value in register or another
// thread_local var...
}
int main(int argc, char **argv)
{
try {
some_function();
} // IMPLEMENTATION:
// if thrown __type_of for std::exception or derived...
catch (const std::exception& e) {
// IMPLEMENTATION:
// e references *(std::exception*)(&__exception_object[0]);
...
}
}
您必须通过引用进行捕获,否则您不可能获得对象的正确动态类型。至于其使用寿命,该标准保证,在[except.throw]
,
异常的最后一个剩余活动处理程序通过重新引发以外的任何方式退出后,或者引用异常对象的最后一个 std::exception_ptr (18.8.5( 类型的对象被销毁,以较晚者为准
通过常量引用捕获正是捕获异常的方式。异常对象不一定存在于"堆栈上"。编译器负责适当的魔术来完成这项工作。
另一方面,您的示例无法编译,因为std::exception
可能只是默认构造或复制构造的。在这种情况下,what()
方法将返回指向空(c 样式(字符串的指针,这不是特别有用。
建议你根据需要抛出一个std::runtime_error
或std::logic_error
,或者从中派生一个类:
-
logic_error
调用方请求了超出服务设计参数的内容。 -
runtime_error
当呼叫者请求了一些合理的东西,但外部因素阻止您满足请求时。
http://en.cppreference.com/w/cpp/error/exception
from except.throw:
抛出异常副本初始化(8.5,12.8(一个临时对象, 调用异常对象。临时是一个左值,用于 初始化在匹配处理程序 (15.3( 中声明的变量。如果 异常对象的类型将是不完整的类型或 指向(可能符合 CV 条件的(void 以外的不完整类型的指针 程序格式不正确。
这是抛出异常的行为,将异常对象复制到任何堆栈之外的异常区域中。因此,通过引用捕获异常是完全合法且可取的,因为异常对象生存期将延长到最后一个可能的catch()
。
- 在提升multi_index容器中,是否定义了"default index"?
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 在对象构造期间,将指向尚未构造的子对象的指针传递给另一个子对象的构造函数是否危险?
- 聚合初始化的 C++17 扩展是否使大括号初始化变得危险?
- 这个危险指针示例是否因为 ABA 问题而存在缺陷?
- 是否存在与将数据流式传输到 c++ 异常类相关的任何危险
- 在构造器的初始化列表中使用"this"对Qt是否特别危险?
- 混合流和标准是否存在技术危险
- 在不先显式调用析构函数的情况下,在旧对象上使用placement new是否危险
- 在这种情况下,使布尔运算符过载是否危险
- 可能同时从不同的线程读取全局变量是否危险
- 优化级别-O3在g++中是否危险
- 矢量自动调整大小在多线程代码中是否是一种危险情况
- 是否危险将 int * 转换为无符号的 int *
- 隐藏第三方命名空间是否危险
- 在UTF-8内部工作,然后仅在Windows中需要时转换为UTF-16,是否存在任何危险
- 通过引用捕获异常是否危险
- 以任何方式将基指针强制转换为派生指针是否危险
- std::reference_wrapper的隐式T&constructor是否会<T>使其使用起来很危险?
- 从安全角度来看,在 Windows 中使用管道是否被认为是危险的