R.3: 原始指针(T*)不应拥有所有权
There is nothing (in the C++ standard or in most code) to say otherwise and most raw pointers are non-owning. We want owning pointers identified so that we can reliably and efficiently delete the objects pointed to by owning pointers.
这一点不存在任何例外(无论是C++标准还是大部分代码)而且大多数原始指针就是没有所有权的。我们需要所有权指针被定义出来,这样就可以可靠地,高效地删除所有权指针指向的对象。
Example(示例)
void f()
{
int* p1 = new int{7}; // bad: raw owning pointer
auto p2 = make_unique<int>(7); // OK: the int is owned by a unique pointer
// ...
}
The unique_ptr protects against leaks by guaranteeing the deletion of its object (even in the presence of exceptions). The T* does not.
unique_ptr通过(即使在发生异常的情况下)保证对象的删除来防止内存泄露。而T*不会。
Example(示例)
template<typename T>
class X {
public:
T* p; // bad: it is unclear whether p is owning or not
T* q; // bad: it is unclear whether q is owning or not
// ...
};
We can fix that problem by making ownership explicit:
我们可以通过明确地赋予所有权来解决这个问题。
template<typename T>
class X2 {
public:
owner<T*> p; // OK: p is owning
T* q; // OK: q is not owning
// ...
};
例外主要来源于既有代码,特别是那些由于ABIs必须保持C兼容性或者包含C或C风格C++接口的情况。事实上存在成百万行的代码违反这条反对T*持有所有权的准则,它们不能被忽略。我们很高兴地看到软件工具可以将20年以上的老代码转换为崭新的现代C++代码,我们鼓励这类工具的开发、部署和使用,我们甚至向这个领域的研究做出了贡献,而且将继续做出贡献。然而这需要时间:“既有代码”的产生快于我们翻新旧代码的速度,因此这个过程将会持续一些年。
This code cannot all be rewritten (even assuming good code transformation software), especially not soon. This problem cannot be solved (at scale) by transforming all owning pointers to unique_ptrs and shared_ptrs, partly because we need/use owning "raw pointers" as well as simple pointers in the implementation of our fundamental resource handles. For example, common vector implementations have one owning pointer and two non-owning pointers. Many ABIs (and essentially all interfaces to C code) use T*s, some of them owning. Some interfaces cannot be simply annotated with owner because they need to remain compilable as C (although this would be a rare good use for a macro, that expands to owner in C++ mode only).
不可能所有的代码都被重写(即使存在优秀的转换软件),很快重写更不可能。这个问题不可能通过将所有具有所有权指针转换为unique_ptr和shared_ptr来解决,一部分的原因是基础的资源持有者需要/使用具有所有权的原始指针(同时也是简单指针)。例如,普通的vector实现包含一个所有权指针和两个非所有权指针。很多ABI(本质上讲所有面向C语言的接口)使用T*,其中有些具有所有权。因为需要维持C语言可编译,因此有些接口不能简单地加注所有权(虽然这是很少见的宏的好用法,它只是在C++模式时展开成为所有权指针)。
Note(注意)
owner<T*> has no default semantics beyond T*. It can be used without changing any code using it and without affecting ABIs. It is simply an indicator to programmers and analysis tools. For example, if an owner<T*> is a member of a class, that class better have a destructor that deletes it.
owner<T*>没有包含任何超过T*的语义。它不修改任何代码就可以使用而且不会影响ABI。它只是面向程序员和分析工具的一个指示信息。例如,如果owner<T*>是某类的成员,那个最好准备一个析构函数来销毁它。
Example, bad(反面示例)
Returning a (raw) pointer imposes a lifetime management uncertainty on the caller; that is, who deletes the pointed-to object?
返回一个(原始)指针会增加调用者生命周期管理的不确定性;即:谁应该销毁指针指向的对象?
Gadget* make_gadget(int n)
{
auto p = new Gadget{n};
// ...
return p;
}
void caller(int n)
{
auto p = make_gadget(n); // remember to delete p
// ...
delete p;
}
In addition to suffering from the problem from leak, this adds a spurious allocation and deallocation operation, and is needlessly verbose. If Gadget is cheap to move out of a function (i.e., is small or has an efficient move operation), just return it "by value" (see "out" return values):
这段代码可以排除来自内存泄漏问题的痛苦,但是增加了虚假的分配和释放操作和没有必要的冗长性。如果Gadget很容易移出函数(也就是说,很小或者存在高效的移动操作),直接只用传值(参照“输出“返回值)。
Gadget make_gadget(int n)
{
Gadget g{n};
// ...
return g;
}
This rule applies to factory functions.
这条准则适用于工厂函数。
Note(注意)
If pointer semantics are required (e.g., because the return type needs to refer to a base class of a class hierarchy (an interface)), return a "smart pointer."
如果需要指针语义(例如因为返回的指针需要指向类层次中的基类(某个接口)),返回一个智能指针。
Enforcement(实施建议)
原文链接:
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#r3-a-raw-pointer-a-t-is-non-owning
觉得本文有帮助?请分享给更多人。
关注【面向对象思考】轻松学习每一天!
面向对象开发,面向对象思考!