1. function call chain
The call relationship between a function and a sub-function can be represented by a chain. The call connection is related to an important data structure: stack frame. When the sub-function returns, the stack is expanded, the Destructor set by the compiler to destroy the object constructed on the stack.
Function call a ----------------------> B ---------------------> C -------------------------> d
2. Exception Handling chain
Similarly, structured exception handling on Windows also has a similar exception handling chain, which is also constructed on the stack, but is independent from the normal function chain. When a sub-function encounters an exception, it cannot return the result through a normal call and analyze the object. In this case, the exception chain is used to trace back and expand the exception call chain, all objects attached to the exception chain are analyzed during the second call of the exception chain (unwind processing, first search for the exception handler.
Exception chain excpnode_a -------> excpnode_ B --------------------------------------------->!! Bomb !!<
Each node contains the destructor, normal call chain, and exception chain of the objects corresponding to the function. These are completed by the C ++ compiler. For each call operation, the C ++ compiler generates a sub-function stack frame node. For each try-catch statement, the C ++ statement constructs an exception link node.
However, if a programmer does not write try-catch in the C function, but the D function has a try-catch statement, and an exception occurs in the D function during running, then, all the objects generated in Function C cannot be destructed.
3. Test the program code
// debugee.cpp : Defines the entry point for the console application.//#include "stdafx.h"class Object{public:Object(const char tag) : m_tag(tag){printf("Object [%c] %p\n", m_tag, this);};~Object(){printf("~Object [%c] %p\n", m_tag, this);};private:char m_tag;};void D(void){Object d('D');*(int *)0 = 0;}void C(void){Object c('C');D();}void B(void){Object b('B');try{C();}catch(...){printf("B caught exception.\n");}}void A(void){Object a('A');try{B();}catch(...){printf("A caught exception.\n");}}int main(int argc, char* argv[]){A();return 0;}
4. Output:
5. Pay attention to differences in compiler implementation
The differences between VC compiler versions and compilation options are marked by/ehs5.
class Object{public: Object(const char tag) : m_tag(tag) {printf("Object [%c] %p\n", m_tag, this);}; ~Object() {printf("~Object [%c] %p\n", m_tag, this);};private: char m_tag;};void E(void){ Object e('E'); //try { throw(""); } //catch (...) { }}void D(void){ Object d('D'); E();}void C(void){ Object c('C'); D();}void B(void){ Object b('B'); //try { C(); } //catch(...) { printf("B caught exception.\n"); }}void A(void){ Object a('A'); try { B(); }catch(...) { printf("A caught exception.\n"); }}int main(int argc, char* argv[]){ A(); return 0;}
The results produced by without/ESCs are as expected