避免资源泄漏的关键是使用智能指针和异常安全设计。1. 使用std::unique_ptr和std::shared_ptr自动管理资源,确保在异常发生时资源能被正确释放;2. 遵循raii原则,将资源绑定到对象生命周期,利用析构函数释放资源;3. 保证基本或强异常安全,确保程序在异常后保持一致状态;4. 封装非内存资源如文件描述符、锁、数据库连接等进raii对象;5. 合理使用try-catch,优先依赖raii而非捕获异常修复状态。

在异常处理过程中,资源泄漏是个常见的问题。尤其在C++这类需要手动管理资源的语言中,一旦抛出异常而没有妥善处理,很容易导致内存、文件句柄等资源无法释放。要避免这种情况,智能指针和合理的异常安全设计是关键。

智能指针:自动释放资源的首选工具
C++11引入了std::unique_ptr和std::shared_ptr,它们能在对象生命周期结束时自动释放所管理的资源,这对异常安全非常有帮助。

-
unique_ptr适用于独占所有权的情况,开销小,效率高。 -
shared_ptr用于共享所有权,通过引用计数来决定何时释放资源。
举个例子:
void processFile() {
std::unique_ptr file = std::make_unique("example.txt");
file->open();
// 假设这里可能抛出异常
file->read();
} 如果read()抛出了异常,file会在栈展开过程中被析构,资源会被自动释放,不会造成泄漏。

使用建议:
- 尽量用
make_unique或make_shared创建智能指针,避免裸指针操作。 - 不要混用智能指针和裸指针管理同一块资源,容易引发重复释放或泄漏。
异常安全设计:让代码在异常下也能保持稳定
异常安全不仅仅是捕获异常那么简单,更重要的是确保程序在异常发生后仍处于一致的状态。
常见做法包括:
- RAII(资源获取即初始化):将资源绑定到对象的生命周期上,利用析构函数自动释放资源。
- 保证基本异常安全(Basic Guarantee)或强异常安全(Strong Guarantee):前者保证程序状态有效但不确定,后者则要求要么成功,要么不改变状态。
比如,在实现一个容器类时,如果你在插入元素前先复制数据,再修改内部结构,就可以做到“强异常安全”。
注意事项:
- 避免在析构函数中抛出异常,否则可能导致程序终止。
- 对于复杂操作,考虑使用“复制并交换”技巧来提升安全性。
注意那些容易忽略的资源类型
很多人只关注内存泄漏,其实还有不少其他类型的资源也容易在异常中被遗漏,比如:
- 文件描述符
- 网络连接
- 互斥锁(mutex)
- 数据库连接
这些资源也需要封装进RAII风格的对象中,例如使用std::lock_guard管理锁,或者自定义一个数据库连接包装类。
举个实际场景:
你在加锁后执行一段可能抛异常的代码,如果没有使用lock_guard,即使你写了unlock(),也可能因为异常跳过这一步,导致死锁。
合理使用try-catch,而不是掩盖问题
虽然你可以用try-catch来捕获异常,但不要滥用它作为“兜底”手段。尤其是在处理资源时,应该优先依赖RAII和智能指针,而不是靠catch去修复状态。
几点建议:
- catch块尽量做清理工作,而不是强行恢复逻辑。
- 如果不能处理异常,就让它继续向上抛。
- 不要在catch中静默吞掉异常,除非你明确知道后果。
基本上就这些。只要合理使用智能指针、遵循RAII原则,并在设计时考虑异常安全级别,就能大大减少资源泄漏的风险。










