[Effective C++]条款29:为“异常安全”而努力是值得的

.
异常安全是C++编程中非常重要的概念,它确保程序在抛出异常时仍能保持数据和状态的一致性。
1、异常安全的三个级别

  • 基本保证: 如果抛出异常,程序处于有效状态,没有资源泄漏,所有对象仍然可用
  • 强保证:如果抛出异常,程序状态与调用函数之前完全相同
  • 不抛异常保证:函数保证不会抛出任何异常

2、场景异常场景和处理方案
2.1、资源泄漏

  • 场景:动态分配内存或打开文件等资源后,在释放前抛出异常
  • 解决方案:使用RAII技术
#include <memory>
#include <fstream>

void processFile() {
    // 使用unique_ptr管理动态内存
    auto data = std::make_unique<int[]>(1000);
    
    // 使用fstream管理文件资源
    std::ofstream file("data.txt");
    if (!file) {
        throw std::runtime_error("无法打开文件");
    }
    
    // 操作...
    // 即使这里抛出异常,资源和文件也会被正确释放
}

2.2、容器操作中的异常

  • 场景:向容器内添加元素时,元素的复制、移动构造函数可能抛出异常
  • 解决方案:使用emplace_back或reserve预先分配空间
#include <vector>
#include <string>

class MyClass {
public:
    MyClass(int x, const std::string& s) : x_(x), s_(s) {}
    // ... 可能有抛出异常的复制/移动操作
private:
    int x_;
    std::string s_;
};

void addToVector() {
    std::vector<MyClass> vec;
    vec.reserve(10); // 预先分配空间,避免多次重分配
    
    try {
        vec.emplace_back(1, "test"); // 原地构造,效率更高
        // ... 其他操作
    } catch (...) {
        // 即使抛出异常,vec也是有效状态
        throw;
    }
}

2.3、事务性操作
场景:需要执行多个操作,要么全部成功,要么全部不执行
解决方案:使用copy-and-swap惯用法

#include <algorithm>

class Database {
public:
    void addUser(const std::string& name, int age) {
        Database temp = *this; // 创建副本
        
        // 在副本上执行可能失败的操作
        temp.users_.emplace_back(name, age);
        temp.updateIndex(name);
        
        // 如果所有操作都成功,交换数据
        std::swap(*this, temp);
    }
    
private:
    std::vector<std::pair<std::string, int>> users_;
    // ... 其他成员和updateIndex方法
};

2.4、锁管理中的异常

  • 场景:获取锁后,在释放前抛出异常
  • 解决方案:使用std::lock_guard或std::unique_lock
#include <mutex>
#include <vector>

std::mutex mtx;
std::vector<int> shared_data;

void modifyData(int value) {
    std::lock_guard<std::mutex> lock(mtx); // 自动释放锁
    
    shared_data.push_back(value);
    // 即使这里抛出异常,锁也会被释放
}

2.5、构造函数的异常安全

  • 场景:构造函数中部分成员初始化后抛出异常
  • 解决方案:使用智能指针或延迟初始化
class ResourceHolder {
public:
    ResourceHolder() 
        : res1(std::make_unique<Resource>()), // 使用智能指针
          res2(nullptr) {                    // 延迟初始化
        try {
            res2 = new Resource(); // 可能抛出异常
        } catch (...) {
            // res1会被unique_ptr自动释放
            throw;
        }
    }
    
    ~ResourceHolder() {
        delete res2;
    }
    
private:
    std::unique_ptr<Resource> res1;
    Resource* res2;
};

3、异常强保证的技巧
3.1、使用std::exchange

#include <utility>

void updateValues(std::vector<int>& data, int newValue) {
    std::vector<int> oldData = data; // 备份
    
    try {
        data.push_back(newValue);
        // 其他可能抛出异常的操作...
    } catch (...) {
        data = std::exchange(oldData, data); // 恢复原状态
        throw;
    }
}

思维导图笔记:
请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值