异常栈展开和对象析构

1、函数调用链

函数与子函数之间的调用关系可以用一个链来表示,调用连,相关的一个重要数据结构就是栈帧,子函数返回的时候,栈展开,构造在栈上的对象被编译器设置的析构函数析构销毁。

 函数调用         A ----------------------> B ---------------------> C ------------------------->D


2、异常处理链

类似的,windows上的结构化异常处理也有一个类似的异常处理链,而且这个链刚好也是在栈上构造的,但是和函数正常的链是各自独立的。当某一个子函数发生异常的时候,就不能走正常的调用连返回,进而析构对象;这个时候,需要走异常链来回溯,展开异常调用链,所有附着在异常链上的对象,在异常链的第二次调用(unwind处理,第一次是搜索异常处理者)的时候析构。

异常链       ExcpNode_A-------> ExcpNode_B------------------------------------------->!!BOMB!!<


每一个节点都包含了该函数对应的对象的析构操作,正常调用链也好,异常链也好,都是这样的,这些是由C++编译器完成的。每一个call操作,c++编译器产生一个子函数栈帧节点。每一个try-catch语句,c++语句构造一个异常链节点。


但是,如果一个程序员在C函数里面没有写try-catch,但是D函数里面有try-catch语句,而且运行的时候,在D函数里面发生了异常,那么C函数里面的生成的所有对象得不到析构的机会。


3、测试程序代码

// 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、输出:



5、注意编译器实现差异

vc编译器的版本差异和编译选项的差异,vc2005的/EHsc标记。

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;
}

不带/EHsc产生的结果符合预期




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值