使用RAII和智能指针可防止异常导致的资源泄露,如FileWrapper封装文件操作,异常发生时析构函数自动调用,确保资源释放。

在C++中,异常可能导致程序提前跳转,从而跳过资源释放代码,造成资源泄露。防止这类问题的关键是利用RAII(Resource Acquisition Is Initialization)机制和智能指针,确保资源的生命周期与对象的生命周期绑定。
使用RAII管理资源
RAII的核心思想是:资源的获取在对象构造时完成,释放则在对象析构时自动进行。只要对象被正确销毁(即使异常发生),析构函数就会被调用。
例如,用类封装文件句柄:
class FileWrapper {
FILE* file;
public:
FileWrapper(const char* path) {
file = fopen(path, "r");
if (!file) throw std::runtime_error("无法打开文件");
}
~FileWrapper() {
if (file) fclose(file);
}
// 禁止拷贝,或实现移动语义
FileWrapper(const FileWrapper&) = delete;
FileWrapper& operator=(const FileWrapper&) = delete;
FILE* get() const { return file; }
};
当函数中创建FileWrapper对象后抛出异常,栈展开会自动调用其析构函数,文件被安全关闭。
立即学习“C++免费学习笔记(深入)”;
优先使用智能指针
对于动态分配的内存,使用std::unique_ptr或std::shared_ptr代替裸指针。
- std::unique_ptr:独占所有权,轻量高效,适用于大多数场景
- std::shared_ptr:共享所有权,带引用计数,适用于需要多处引用的情况
示例:
void process() {
auto ptr = std::make_unique();
do_something(); // 若此处抛异常,ptr仍会被自动释放
}
避免在构造函数中执行可能失败的操作
如果类的构造函数需要分配资源并可能抛出异常,应确保已分配的资源能被清理。更推荐的做法是将资源初始化逻辑分离,或使用“两阶段初始化”。
更好的方式是使用工厂函数或静态创建函数:
std::unique_ptropenFile(const char* path) { auto file = std::make_unique (path); return file; }
确保自定义资源类型具有异常安全的析构函数
析构函数不应抛出异常。如果必须调用可能失败的操作(如关闭网络连接),应在析构函数中捕获异常并处理,而不是传播。
例如:
~NetworkConnection() {
try {
close_socket();
} catch (...) {
// 记录日志,但不抛出
}
}
基本上就这些。只要坚持使用RAII和标准库提供的资源管理工具,大多数资源泄露问题都能自动避免。关键是不要手动调用delete或释放资源,而是依赖对象的生命周期管理。










