stack unwinding & on_scope_exit & c++ exception:构造 析构 异常处理

C++对exception的支持需要编译器,操作系统和处理器的协同支持:
1.编译器需为每一个函数生成必要信息用以支持异常处理(在一个call stack中的所有函数的这些信息被保存在一个list中),编译器生成处理异常的回调函数等等。
2.操作系统需要在发生异常时调用异常处理函数,在异常处理函数中检查这个list以匹配对应catch block以及析构在这个范围内的局部变量等等。
3. 处理器需要有相应的寄存器保存异常处理函数的地址,以及软中断等等。。。

太复杂了,看链接吧:https://www.codeproject.com/Articles/2126/How-a-C-compiler-implements-exception-handling

在构造函数 or 析构函数中抛异常:

  • 在构造函数中可以抛出异常,但是该对象的析构函数不会被调用(因为异常导致该对象没有被正确生成,也就没有必要对其析构),所以,某些成员变量或者其他需要被清理的工作应该考虑 on_scope_exit 处理,例如,将成员变量定义为智能指针,并将分配到的内存第一时间放入智能指针中,这样即使对象的析构函数不被调用也不会出现内存泄露(成员变量的析构会正常执行)。
void f()
{
     X x;             // If X::X() throws, the memory for x itself will not leak
     Y* p = new Y();  // If Y::Y() throws, the memory for *p itself will not leak
}

这里判断一个对象有没有被构造完成,需要考虑其是否使用了委托构造(delegating constuctors),委托构造结束也就表示该对象已经构造完成, see link: https://stackoverflow.com/questions/14386840/destructor-called-after-throwing-from-a-constructor

总结就是:
>When an exception is thrown from a constructor, the object is not considered instantiated, and therefore its destructor will not be called. But all destructors of already successfully constructed base and member objects of the same master object will be called. Destructors of not yet constructed base or member objects of the same master object will not be executed. 

见链接:https://en.wikibooks.org/wiki/C%2B%2B_Programming/Exception_Handling
  • 不能在析构函数中抛出异常,因为,如果是由于之前的代码抛出的异常导致的这次析构——即stack unwinding,那么,再次抛出(在析构函数体中)将会导致程序直接退出。

stack unwinding:

on_scope_exit 机制的依赖于 stack unwinding 的实现原理:

The objects allocated on the stack(函数 or 作用域中的局部变量) are “unwound” when the scope is exited(函数返回 or 离开作用域). This is done by the compiler inserting calls to destructors of local(stack) variables.

This process of destroying local objects and calling destructors is called stack unwinding。

This means that the stack unwinding process should never throw an exception (either use only code guaranteed not to throw in destructors, or surround everything in destructors with try { and } catch(…) {})

see link: http://the-witness.net/news/2012/11/scopeexit-in-c11/

捕捉时异常需要注意:

异常都是以传值(复制生成临时对象)的方式被throw出去的,但是,如果catch的时候参数也是传值,会导致派生类和基类的slicing问题。所以,catch语句应该使用引用来捕捉这个临时对象,constnon-const都可以,根据使用场景而定。

再来复制一段 more effective c++的话总结:

  • 第一,exception object 总是会被复制,如果以 by value 方式捕捉,它们甚至被复制两次(并可能导致slicing问题)。所以,一定要 catch by reference。
  • 第二,“被抛出称为 exception”的对象,其被允许的类型转换动作,比“被传递到函数去”的对象少。(也就是说,函数参数存在很多显式或者隐式转换;但是,exception的catch语句的参数没有那么多转换,例如,抛出派生类,catch的参数是基类,这是可以的。但是链接中的这样是不行的。)。
  • 第三,catch 子句以其“出现于源代码的顺序”被编译器检验对比,其中第一个匹配成功者便执行;而当我们以某对象调用一个虚函数,被选中执行的是那个“与对象类型最佳匹配吻合”的函数,不论是不是源代码所列的第一个。

最后一句话,貌似 google coding style 不推荐写 exception,会导致代码执行混乱。

参考:

https://isocpp.org/wiki/faq/exceptions#ctors-can-throw
https://isocpp.org/wiki/faq/exceptions#selfcleaning-members
https://stackoverflow.com/questions/2522299/c-catch-blocks-catch-exception-by-value-or-reference
https://stackoverflow.com/questions/2343208/can-you-catch-an-exception-by-the-type-of-a-conversion-operator
https://stackoverflow.com/questions/2331316/what-is-stack-unwinding
https://stackoverflow.com/questions/14386840/destructor-called-after-throwing-from-a-constructor

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值