C++抛出异常后仍能调用作用域上文局部变量的析构方法

先贴现象:

#include <iostream>
#include <string>
using namespace std;
class MyExcept
{
public:
	explicit MyExcept(string err) : errmsg(err) {}
	string getErrMsg() { return errmsg; }
private:
	string errmsg;
};

class Demo{
public:
    string field;
	Demo (string val):field(val) {}
	~Demo(){
		cout << "Will destructor be called for "+field << endl;
	}
};

int main(int argc, char* argv[]){
    Demo d("papa"),d2("auau");
    // throw MyExcept("Throw exception in member function");
	try{
		Demo d("heihei"),d2("haha");
        throw MyExcept("Throw exception in member function");
        /*
            抛出异常后将逆序调用try域内局部变量的析构函数
            ,然后才进入catch。前提是该异常要被try...catch捕获,
            如果没有捕获异常就不能析构try域以及try域外上文作用域的局部变量
        */
	}catch (MyExcept& e){
		cout << e.getErrMsg() << endl;
	}
	return 0;
}

输出:

VMCatalina:Exception haypin$ ./main
Will destructor be called for haha
Will destructor be called for heihei
Throw exception in member function
Will destructor be called for auau
Will destructor be called for papa
VMCatalina:Exception haypin$

不捕获异常:


int main(int argc, char* argv[]){
    Demo d("papa"),d2("auau");
    throw MyExcept("Throw exception in member function");
	return 0;
}

输出:

VMCatalina:Exception haypin$ clang++ -g except.cpp -o main
VMCatalina:Exception haypin$ ./main
libc++abi.dylib: terminating with uncaught exception of type MyExcept
Abort trap: 6
VMCatalina:Exception haypin$ 

可以看到:只要捕获了异常,则抛出异常位置的上文作用域(包括try域以及外层的上文作用域)的局部变量将被逆序析构。如果不捕获异常就不会析构,从而造成内存泄漏

https://stackoverflow.com/questions/8311457/are-destructors-called-after-a-throw-in-c的翻译:

https://segmentfault.com/q/1010000002498987

Q51:C++在抛出一个异常后是否会调用析构函数?

我运行了一个样例并且栈分配的对象的析构函数确实被调用了,但这能被标准保证么?

A71:是的,这是被保证的(前提是异常要被捕获)。

C++11 15.2 Constructors and destructors 构造器和析构器[except.ctor]

1 As control passes from a throw-expression to a handler, destructors are invoked for all automatic objects constructed since the try block was entered. The automatic objects are destroyed in the reverse order of the completion of their construction.

1、只要将throw表达式的控制传递到一个处理(这指的是捕获throw吧),那么从进入try块的所有自动构造的对象的析构函数都将被调用。这些自动对象按照它们构造完成的顺序逆序被销毁

进一步,如果在对象构造期间抛出了异常,则部分构造的对象的子对象也能保证被正确析构

2 An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object’s destructor will be invoked. If the object was allocated in a new-expression, the matching deallocation function (3.7.4.2, 5.3.4, 12.5), if any, is called to free the storage occupied by the object.

2、由于一个异常导致一个对象在其存储期间(storage duration,指生成)的初始化或构造被终止,那将为它的已经完全构造过的子对象调用析构函数,除了那些类似联合类的变体成员字段。也就是那些主构造器已经完成执行但析构器还没有开始执行的子对象。

相似地,如果一个对象的非代表性构造器已经完成执行但其代表性的构造器由于异常而退出,那么该对象的析构器将被调用。如果对象在一个new表达式中被分配,则匹配的解除分配函数(如果有的话),将被调用来释放对象占用的存储空间。

示例:在构造函数中已经完成部分子对象的构造后抛出异常:

#include <iostream>
#include <string>
using namespace std;
class MyExcept
{
public:
	explicit MyExcept(string err) : errmsg(err) {}
	string getErrMsg() { return errmsg; }
private:
	string errmsg;
};

class Demo{
public:
    string field;
	Demo (string val):field(val) {}
	~Demo(){
		cout << "Will destructor be called for "+field<< endl;
	}
};
class C2{
public:
    Demo field;
    C2(string val):field(val){
        throw MyExcept("Throw exception in member function");
    };
};
int main(int argc, char* argv[]){
	try{
		C2 c("heihei");//,d2("haha");
        /*
            部分初始化的对象在其构造函数中抛出异常,将析构其子对象
        */
	}catch (MyExcept& e){
		cout << e.getErrMsg() << endl;
	}
	return 0;
}

输出:

VMCatalina:Exception haypin$ clang++ -g except.cpp -o main
VMCatalina:Exception haypin$ ./main
Will destructor be called for heihei
Throw exception in member function
VMCatalina:Exception haypin$

动态申请内存:


int main(int argc, char* argv[]){
	try{
        C2 *pc=new C2("papa");
        /*
            部分初始化的对象在其构造函数中抛出异常,将析构其子对象
        */
	}catch (MyExcept& e){
		cout << e.getErrMsg() << endl;
	}
	return 0;
}

输出:

VMCatalina:Exception haypin$ clang++ -g except.cpp -o main
VMCatalina:Exception haypin$ ./main
Will destructor be called for papa
Throw exception in member function
VMCatalina:Exception haypin$

1和2整个过程被称为"栈解旋"

3、对从一个try块开始到一个throw表达式之间的路径上自动构造的对象调用析构器的处理被称为"stack unwinding"栈解旋。如果在栈解旋期间调用了一个构造器并且由于一个异常而退出,将调用std::terminate(15.5.1)。

。。。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值