悠悠楠杉
如何编写异常安全的C++代码:强异常安全保证的实现方法
一、异常安全的基本概念
异常安全代码的核心在于:当异常被抛出时,程序不会泄漏资源,且能维持数据一致性。C++标准定义了三个级别的异常安全保证:
- 基本保证:程序保持有效状态(无资源泄漏)
 - 强保证:操作要么完全成功,要么回滚到初始状态
3.不抛异常保证:操作保证不会失败(如标记为noexcept的函数) 
cpp
// 弱安全示例(可能泄漏资源)
void unsafe_op() {
    int* ptr = new int[100];
    throw std::runtime_error("Oops");
    delete[] ptr; // 永远不会执行
}
二、实现强异常安全的核心技术
1. RAII(资源获取即初始化)
C++最强大的武器之一,通过对象生命周期自动管理资源:
cpp
class FileHandle {
    FILE* f;
public:
    explicit FileHandle(const char* name) : f(fopen(name, "r")) {
        if(!f) throw std::runtime_error("Open failed");
    }
    ~FileHandle() { if(f) fclose(f); }
    // 禁用拷贝(或实现深拷贝)
};
2. Copy-and-Swap惯用法
实现强保证的经典模式,尤其适用于赋值操作:
cpp
class String {
    char* data;
    size_t length;
void swap(String& other) noexcept {
    std::swap(data, other.data);
    std::swap(length, other.length);
}
public:
    String& operator=(const String& rhs) {
        String temp(rhs); // 可能抛出异常
        swap(temp);       // 不会抛出
        return *this;     // temp析构释放旧资源
    }
};
3. 事务性操作
将多个操作包装为原子性事务:
cpp
class Database {
    std::vector
void commit_update(Record new_rec) {
    auto old_state = records;  // 1. 保存状态
    try {
        records.push_back(new_rec); // 2. 尝试修改
        // 其他可能失败的操作...
    } catch(...) {
        records = std::move(old_state); // 3. 回滚
        throw;
    }
}
};
三、现代C++的最佳实践
1. 智能指针的合理使用
cpp
void safe_operation() {
    auto ptr = std::make_unique<Resource>(); // 自动管理内存
    process(*ptr); // 可能抛出异常
    // 无需手动delete
}
2. 移动语义优化
cpp
class Buffer {
    std::unique_ptr<char[]> data;
public:
    Buffer(Buffer&&) noexcept = default;
    Buffer& operator=(Buffer&&) noexcept = default;
    // 移动操作应标记为noexcept
};
3. noexcept的正确使用
cpp
void critical_operation() noexcept {
    // 保证不会抛出的操作
    std::array<int, 100> stack_array;
    // ... 仅包含不会抛出的操作
}
四、典型场景解决方案
1. 多阶段初始化的处理
cpp
class ResourceManager {
    std::uniqueptr
void initialize() {
    auto temp_conn = std::make_unique<Connection>();
    auto temp_cache = std::make_unique<Cache>(*temp_conn);
    // 全部成功后才提交
    conn = std::move(temp_conn);
    cache = std::move(temp_cache);
}
};
2. 异常安全的数据结构
cpp
template
class SafeVector {
    std::uniqueptr<T[]> data;
    sizet size;
public:
    void pushback(const T& item) {
        auto newdata = std::makeunique<T[]>(size + 1);
        std::copyn(data.get(), size, newdata.get());
        newdata[size] = item; // 可能抛出
        data.swap(new_data);
        ++size;
    }
};
五、测试异常安全性的方法
- 使用异常注入测试
 - 验证资源泄漏(如Valgrind)
 - 检查对象不变式
 
cpp
TEST(ExceptionSafetyTest, StrongGuarantee) {
    Widget w;
    try {
        throw_at_counter = 3; // 模拟在第3步抛出
        w.modify();
        FAIL() << "Should have thrown";
    } catch(...) {
        ASSERT_EQ(original_state, w); // 验证状态回滚
    }
}
结语
实现强异常安全需要开发者:1) 优先使用RAII管理资源 2) 隔离可能失败的操作 3) 通过事务语义保证原子性。现代C++的工具链(智能指针、移动语义等)大大降低了实现成本,但核心思想仍是:在修改状态前准备好所有资源,要么全部提交,要么全部回滚。
"好的异常安全代码不是偶然实现的,而是通过严格的设计原则和惯用法精心构建的。" —— Herb Sutter
                                            
                
                        