[Effective C++]条款08:别让异常逃离析构函数

1、C++中,class的析构函数不应该抛出异常主要原因有:
1.1、 栈展开(Stack Unwinding)问题

当异常抛出时,C++会进行栈展开,调用局部对象的析构函数。如果析构函数本身抛出异常,程序将面临两个异常同时存在的情况,这会触发std::terminate,导致程序终止。
当C++程序抛出异常时,会启动栈展开过程。栈展开的目的是销毁当前作用域内的局部对象,并调用它们的析构函数,以确保资源被正确释放。
如果在栈展开过程中,某个析构函数又抛出了一个新的异常,那么此时会有两个异常同时存在:

  • 一个是正在处理的原始异常;
  • 另一个是析构函数中抛出的新异常。

C++标准规定,不允许同时存在两个活跃的异常。因此,程序会调用 std::terminate,直接终止程序。

  • 例子:程序会调用std::terminate,导致崩溃,而不是捕获异常
#include <iostream>
#include <stdexcept>

class MyClass {
public:
    ~MyClass() noexcept(false) {
        // 析构函数中抛出异常
        throw std::runtime_error("Exception in destructor");
    }
};

int main() {
    try {
        MyClass obj;
        // obj 离开作用域时,析构函数会被调用
    } catch (const std::exception& e) {
        std::cerr << "Caught exception: " << e.what() << std::endl;
    }

    return 0;
}

1.2、资源泄漏风险
如果析构函数抛出异常,可能导致资源未正确释放,因为异常会中断析构函数的正常执行流程。
1.3、违反异常安全原则
析构函数应确保资源释放,抛出异常会破坏这一原则,增加程序的不确定性。
2、处理方式

2.1、捕获并处理异常

  • 在析构函数中捕获可能抛出的异常,确保异常不会传播到析构函数外部
  • 将函数标记为noexcept,确保不会抛出异常,如果异常未捕获,程序会调用std::terminate
class MyClass {
public:
    ~MyClass() noexcept {
        try {
            // 可能抛出异常的代码
            someMethodThatMayThrow();
        } catch (const std::exception& e) {
            // 记录异常或处理异常
            std::cerr << "Exception in destructor: " << e.what() << std::endl;
        } catch (...) {
            // 处理未知异常
            std::cerr << "Unknown exception in destructor" << std::endl;
        }
    }

private:
    void someMethodThatMayThrow() {
        // 可能抛出异常的操作
    }
};

2.2、避免在析构函数中调用可能抛出异常的方法

  • 将可能抛出异常的操作一道析构函数中,可以类的公共方法中调用,在析构函数中只处理不会抛出异常的操作
class MyClass {
public:
    void cleanup() {
        // 可能抛出异常的代码
        someMethodThatMayThrow();
    }

    ~MyClass() noexcept {
        // 只处理不会抛出异常的操作
    }

private:
    void someMethodThatMayThrow() {
        // 可能抛出异常的操作
    }
};

3、总结

  • 析构函数绝对不要触发异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉该异常,然后捕获他们,不进行异常传播或结束程序
  • 如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作

思维导图笔记
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值