CppCoreGuidelines P.8 资源不可泄漏
P.8 Don’t leak any resources
理由
如果系统长期运行的,哪怕是没有及时释放的很小的资源,也有可能耗尽系统的资源。所以,对于长期运行的系统,切不可泄漏资源。另外,及时释放系统资源也是一种负责任的编程态度。
例子
void f(char* name) { FILE* input = fopen(name, "r"); // ... if (something) return; // bad: if something == true, a file handle is leaked // ... fclose(input); }
以上例程中,如果 something
为真,函数直接返回了,没有及时关闭 input
文件结构体,产生资源泄漏。
应该使用 RAII 模式。RAII = Resource Acquisition Is Initialization 意思是分配资源的时候初始化,资源使用结束后,自动释放。
void f(char* name) { ifstream input {name}; // ... if (something) return; // OK: no leak // ... }
改用 ifstream
文件输入对象可以避免资源泄漏。当函数返回的时候,自动调用析构函数释放内存数据。
注意
所谓的泄漏( leak )通俗的讲,就是出现资源没有及时释放的情况。细分下去,更重要的一类泄漏是:存在无法被释放的资源。比如,你在内存堆中创建了一个对象,然后在程序运行过程中,该对象的地址指针被其他数据覆盖掉了。因为这个对象的地址信息丢失了,你就无法释放这个对象了。所以,对任何中途结束的函数,要么及时释放内存,要么把函数分配的对象返回给上层函数继续使用或者晚些时候释放。
不过,我们也不要求你在程序结束的时候一定要返回某个常驻对象的地址。因为程序结束的时候,系统会自动清理分配给该程序的内存。
然而,最简单、最安全的方法还是应该利用抽象机制隐式地释放资源。
注意
通过强化“生命周期安全规范”,可以避免资源泄漏。再结合 RAII,就不需要垃圾回收机制。再结合“类型和边界检测规范”,我们就可以通过工具实现完整的类型和资源安全。
强化
- 检查指针的使用。区分有主和无主指针。如以上例子用标准库资源输入流对象替换文件句柄。或者可能的话,用 GSL 中的
owner
所有权指针标记资源所有权。 检查原始的
new
和delete
操作。考虑使用定义了释放规则的接口封装:shared_ptr<int> p(new int[10], [](int* p) {delete []p;})
- 检查返回值是原始指针(raw pointer)的资源分配函数。比如:
fopen
,malloc
,strdup
.