CppCoreGuidelines I.11 切不可通过原始指针(T*)或引用(T&)转移所有权
25 March 2022
I.11: Never transfer ownership by a raw pointer (T*
) or reference (T&
)
理由
如果不清楚某对象的所有权是属于调用者还是被调用者,可能会导致内存泄漏,或不完全的对象析构。
例子
struct X { int x; }; X* compute(int y) // don't { X* res = new X{}; // ... cout << res << endl; return res; } int main() { auto xx = compute(0); cout << xx; return 0; }
0x208072a27f0 0x208072a27f0
谁来删除函数 compute
返回的 X
?如果 compute
返回的是引用,则此问题更难定位。考虑数值返回。如果返回结果很大,可以使用移动机制。
vector<double> compute(int x) // good { vector<double> res(10000); // ... res[0] = x; return res; } int main() { auto xx = compute(999); cout << xx[0]; return 0; }
999
替代方案
通过智能型指针传递所有权。比如unique_ptr
(所有权互斥), shared_ptr
(所有权共享)。然而,这个方法并不优雅,相比返回对象本身,性能可能会差。
替代方案
有时候因为 ABI 兼容要求,无法修改老的代码,或因为资源不足。这时,可以使用 GSL 的 owner
标注指针所有者。
#include <gsl/gsl> using namespace gsl; struct X {int x;}; owner<X*> compute(int x) // It is now clear that ownership is transferred { owner<X*> res = new X{}; // ... cout << res << endl; return res; } int main() { auto xx = compute(10); cout << xx; return 0; }
0x20d54de27f0 0x20d54de27f0
这里我们告诉代码分析工具, res
是一个 owner
。它的值必须删除或转移给其他所有者。这里通过 return
传递所有权。
注意
以原始指针传递的对象,其所有者默认就是调用者。因此,其生存周期由调用者处理。从另一个角度看,所有权转移的接口比指针传递接口用的更少,所以默认是不进行所有权转移的。
强化
- (简单)
delete
非owner<T>
的原始指针的时候,发出警告。建议使用标准库的资源处理程序,或使用owner<T>
- (简单) 在重置或显示删除有主指针的时候,发出警告。
- (简单)返回值是
new
或函数调用返回值是owner
指针但是赋给原始指针或无主引用的时候,发出警告。