C++ 异常—堆栈的释放与清理

    当某段代码抛出一个异常时,会在堆栈中寻找catch处理程序。Catch处理程序可以是在堆栈执行的0个或者多个函数调用。当发现一个catch时,堆栈会释放所有中间堆栈帧,直接跳到定义catch处理程序的堆栈层。堆栈释放(stack unwinding)意味着调用所有具有局部作用域的名称的析构函数,并忽略在当前执行点之前的每个函数中所有的代码。

       然而当堆栈释放时,并不释放指针变量,也不会执行其他清理。这一行为会引发问题,下面的代码演示了这一点:

// Project.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
void funcOne();
void funcTwo();

int main()
{
	try
	{
		funcOne();
	}
	catch (const std::exception& e)
	{
		cerr << "Exception caught!" << endl;
	}
    return 0;
}

void funcOne()
{
	string str1;
	string* str2 = new string();
	funcTwo();
	delete str2;
}

void funcTwo()
{
	ifstream istr;
	istr.open("filename");
	throw exception();
	istr.close();
}


funcTwo()抛出一个异常时,最近的异常处理程序在main()中。控制立刻从funcTwo()的这一行:

    throw exception();

    跳转到main()的这一行:

    cerr << "Exception caught!" << endl;

    funcTwo()中,控制依然在抛出异常的那一行,因此后面的行永远不会有机会运行:

    Istr.close();

然而幸运的是,因为istr是堆栈中的局部变量,所以会调用ifstream析构函数。Ifstream析构函数会自动关闭文件,因此在此不会泄露资源,如果动态分配了istr,那么这个指针不会销毁,文件也不会关闭。

funcOne()中,控制在funcTwo()的调用中,因此后面的行永远不会有机会执行:

delete str2;

在此情况下,确实会发生内存泄露。堆栈释放不会自动调用str2delete。然而,str1会正确的销毁,因为这是堆栈中的局部变量。堆栈释放会正确地销毁所有局部变量。

 

使用智能指针:

    如果基于堆栈的内存分配不可用,就应该使用智能指针。在处理异常时,智能指针可以使编写的代码自动防止内存或者资源泄漏。智能指针对象在堆栈中分配,无论什么时候销毁智能指针对象,都会释放底层的资源。下面使用智能指针unique_ptr重写了前面的funcOne()函数:

#include<memory>
using namespace std;
void funcOne()
{
	string str1;
	auto str2 = make_unique<string>("hello");
	funcTwo();
}

    当从funcOne()返回或者抛出异常时,将自动删除string*类型的str2指针。

    注意:使用智能指针时,永远不必考虑释放底层的资源;智能指针的析构函数将会自动完成这一操作,无论是正常退出函数,还是抛出异常退出函数。


捕获、清理并重新抛出

    避免内存和资源泄露的另一种技术是对于每个函数,捕获可能抛出的所有异常,执行必要的清理,并重新抛出异常,供堆栈种更高层的函数处理。

void funcOne()
{
	string str1;
	string *str2 = new string();
	try {
		funcTwo();
	}
	catch (...) {
		delete str2;
		throw;//Rethrow the exception
	}
	delete str2;
}


这个函数用异常处理程序封装了funcTwo()的调用,处理程序执行清理(在str2上调用delete)并重新抛出异常。关键字throw本身会重新抛出最近捕获的任何异常。注意catch语句使用了...语法捕获所有异常。

这一方法运行良好,但是有点繁琐。特别需要需要注意的是,现在有两行完全相同的代码在str2上调用delete:一行用来处理异常,另一方在函数正常退出的情况下执行。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值