.
异常安全是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;
}
}
思维导图笔记: