好吧,上次我以编写 C++ 为生时,std::auto_ptr 是所有可用的 std 库,而 boost::shared_ptr 则风靡一时。我从未真正研究过 boost 提供的其他智能指针类型。据我所知,C++11 现在提供了 boost 提出的一些类型,但不是全部。
那么有人有一个简单的算法来确定何时使用哪个智能指针吗?最好包括有关哑指针(原始指针,如 T*
)和其余 boost 智能指针的建议。 (像这个这样的东西会很棒)。
Ok, so the last time I wrote C++ for a living, std::auto_ptr
was all the std lib had available, and boost::shared_ptr
was all the rage. I never really looked into the other smart pointer types boost provided. I understand that C++11 now provides some of the types boost came up with, but not all of them.
So does someone have a simple algorithm to determine when to use which smart pointer? Preferably including advice regarding dumb pointers (raw pointers like T*
) and the rest of the boost smart pointers. (Something like this would be great).
发布评论
评论(4)
共享所有权:
采用的标准的
shared_ptr
和weak_ptr
与它们的 增强对应项。当您需要共享资源并且不知道哪个资源将是最后一个存活时,请使用它们。使用weak_ptr来观察共享资源而不影响其生命周期,不破坏循环。通常不会发生带有shared_ptr
的循环 - 两个资源不能互相拥有。请注意,Boost还提供
shared_array
,这可能是shared_ptr 的合适替代方案。常量>
。接下来,Boost 提供
intrusive_ptr
,如果您的资源已经提供引用计数管理并且您希望将其采用 RAII 原则,那么这是一个轻量级解决方案。该标准未采用该标准。独特的所有权:
Boost 还有一个
scoped_ptr
,不可复制且无法为其指定删除程序。std::unique_ptr
是boost::scoped_ptr
的增强版,并且应该是您当您需要智能指针时的默认选择。它允许您在其模板参数中指定删除器,并且是可移动的,这与 boost::scoped_ptr 不同。只要您不使用需要可复制类型的操作(显然),它也可以在 STL 容器中完全使用。再次注意,Boost 有一个数组版本:
scoped_array< /code>
,该标准通过要求
std::unique_ptr
部分专业化来统一delete[]
指针而不是delete
它(使用default_delete
r)。std::unique_ptr
还提供operator[]
而不是operator*
和operator->
代码>.请注意,
std::auto_ptr
仍在标准中,但已被弃用。§D.10 [depr.auto.ptr]
无所有权:
当您知道资源将比引用对象/作用域活得更久时,请使用哑指针(原始指针)或对资源的非拥有引用。当您需要可空性或可重置性时,首选引用并使用原始指针。
如果您想要对资源进行非拥有引用,但您不知道该资源是否会比引用它的对象寿命更长,请将资源打包在
shared_ptr
中并使用weak_ptr< /code> - 您可以使用
lock
测试父shared_ptr
是否处于活动状态,如果资源存在,它将返回非 null 的shared_ptr
仍然存在。如果想测试资源是否已失效,请使用expired
。两者听起来很相似,但在并发执行时却有很大不同,因为expired
仅保证该单个语句的返回值。像这样看似无辜的测试是潜在的竞争条件。
Shared ownership:
The
shared_ptr
andweak_ptr
the standard adopted are pretty much the same as their Boost counterparts. Use them when you need to share a resource and don't know which one will be the last to be alive. Useweak_ptr
to observe the shared resource without influencing its lifetime, not to break cycles. Cycles withshared_ptr
shouldn't normally happen - two resources can't own each other.Note that Boost additionally offers
shared_array
, which might be a suitable alternative toshared_ptr<std::vector<T> const>
.Next, Boost offers
intrusive_ptr
, which are a lightweight solution if your resource offers reference-counted management already and you want to adopt it to the RAII principle. This one was not adopted by the standard.Unique ownership:
Boost also has a
scoped_ptr
, which is not copyable and for which you can not specify a deleter.std::unique_ptr
isboost::scoped_ptr
on steroids and should be your default choice when you need a smart pointer. It allows you to specify a deleter in its template arguments and is movable, unlikeboost::scoped_ptr
. It is also fully usable in STL containers as long as you don't use operations that need copyable types (obviously).Note again, that Boost has an array version:
scoped_array
, which the standard unified by requiringstd::unique_ptr<T[]>
partial specialization that willdelete[]
the pointer instead ofdelete
ing it (with thedefault_delete
r).std::unique_ptr<T[]>
also offersoperator[]
instead ofoperator*
andoperator->
.Note that
std::auto_ptr
is still in the standard, but it is deprecated.§D.10 [depr.auto.ptr]
No ownership:
Use dumb pointers (raw pointers) or references for non-owning references to resources and when you know that the resource will outlive the referencing object / scope. Prefer references and use raw pointers when you need either nullability or resettability.
If you want a non-owning reference to a resource, but you don't know if the resource will outlive the object that references it, pack the resource in a
shared_ptr
and use aweak_ptr
- you can test if the parentshared_ptr
is alive withlock
, which will return ashared_ptr
that is non-null if the resource still exists. If want to test whether the resource is dead, useexpired
. The two may sound similar, but are very different in the face of concurrent execution, asexpired
only guarantees its return value for that single statement. A seemingly innocent test likeis a potential race condition.
决定使用什么智能指针是一个所有权问题。在资源管理方面,如果对象 A 控制着对象 B 的生命周期,则它拥有对象 B。例如,成员变量由其各自的对象拥有,因为成员变量的生命周期是绑定的到对象的生命周期。您可以根据对象的拥有方式来选择智能指针。
请注意,软件系统中的所有权与所有权是分开的,因为我们会在软件之外考虑它。例如,一个人可能“拥有”他们的家,但这并不一定意味着
Person
对象可以控制House
对象的生命周期。将这些现实世界的概念与软件概念混为一谈,肯定会让自己陷入困境。如果您拥有该对象的唯一所有权,请使用
std::unique_ptr
。如果您共享该对象的所有权...
- 如果所有权没有循环,请使用
std::shared_ptr
。- 如果存在循环,请定义一个“方向”,并在一个方向上使用
std::shared_ptr
,在另一个方向上使用std::weak_ptr
。如果该对象拥有您,但有可能没有所有者,请使用普通指针
T*
(例如父指针)。如果该对象拥有您(或以其他方式保证存在),请使用引用
T&
。警告:请注意智能指针的成本。在内存或性能有限的环境中,仅使用普通指针和更手动的内存管理方案可能会很有帮助。
成本:
示例:
二叉树不拥有其父级,但树的存在意味着其父级的存在(或根的
nullptr
),因此使用普通指针。二叉树(具有值语义)对其子树拥有唯一的所有权,因此它们是std::unique_ptr
。在这里,列表节点拥有其下一个和上一个列表,因此我们定义一个方向,并使用
shared_ptr
表示 next 并使用weak_ptr
表示 prev 来打破循环。Deciding what smart pointer to use is a question of ownership. When it comes to resource management, object A owns object B if it is in control of the lifetime of object B. For example, member variables are owned by their respective objects because the lifetime of member variables is tied to the lifetime of the object. You choose smart pointers based on how the object is owned.
Note that ownership in a software system is separate from ownership as we would think of it outside of software. For example, a person might "own" their home, but that doesn't necessarily mean that a
Person
object has control over the lifetime of aHouse
object. Conflating these real world concepts with software concepts is a sure-fire way to program yourself into a hole.If you have sole ownership of the object, use
std::unique_ptr<T>
.If you have shared ownership of the object...
- If there are no cycles in ownership, use
std::shared_ptr<T>
.- If there are cycles, define a "direction" and use
std::shared_ptr<T>
in one direction andstd::weak_ptr<T>
in the other.If the object owns you, but there is potential of having no owner, use normal pointers
T*
(e.g. parent pointers).If the object owns you (or otherwise has guaranteed existence), use references
T&
.Caveat: Be aware of the costs of smart pointers. In memory or performance limited environments, it could be beneficial to just use normal pointers with a more manual scheme for managing memory.
The costs:
std::shared_ptr
has the overhead of a reference count increment on copy, plus a decrement on destruction followed by a 0-count check with deletion of the held object. Depending on the implementation, this can bloat your code and cause performance issues.Examples:
A binary tree does not own its parent, but the existence of a tree implies the existence of its parent (or
nullptr
for root), so that uses a normal pointer. A binary tree (with value semantics) has sole ownership of its children, so those arestd::unique_ptr
.Here, the list node owns its next and previous lists, so we define a direction and use
shared_ptr
for next andweak_ptr
for prev to break the cycle.始终使用
unique_ptr
,除非需要引用计数,在这种情况下使用shared_ptr
(对于极少数情况,使用weak_ptr ;
以防止引用循环)。几乎在所有情况下,可转让的独特所有权就可以了。原始指针:仅当您需要协变返回时才有效,这种情况可能会发生非拥有指向。否则它们并不是很有用。
数组指针:
unique_ptr
对T[]
有专门化,它会自动对结果调用delete[]
,因此您可以安全地执行unique_ptr; p(new int[42]);
例如。shared_ptr
您仍然需要自定义删除器,但不需要专门的共享或唯一数组指针。当然,无论如何,这些东西通常最好用 std::vector 替换。不幸的是,shared_ptr
不提供数组访问函数,因此您仍然需要手动调用get()
,但是unique_ptr
code> 提供operator[]
而不是operator*
和operator->
。无论如何,你必须自我检查。这使得shared_ptr
的用户友好性稍差,尽管可以说通用优势和无 Boost 依赖性使unique_ptr
和shared_ptr
再次成为赢家。作用域指针:通过
unique_ptr
变得无关紧要,就像auto_ptr
一样。真的没有什么更多的了。在没有移动语义的 C++03 中,这种情况非常复杂,但在 C++11 中,建议非常简单。
其他智能指针仍然有用途,例如
intrusive_ptr
或interprocess_ptr
。然而,它们非常小众,并且在一般情况下完全没有必要。Use
unique_ptr<T>
all the time except when you need reference counting, in which case useshared_ptr<T>
(and for very rare cases,weak_ptr<T>
to prevent reference cycles). In almost every case, transferrable unique ownership is just fine.Raw pointers: Good only if you need covariant returns, non-owning pointing which can happen. They're not terrifically useful otherwise.
Array pointers:
unique_ptr
has a specialization forT[]
which automatically callsdelete[]
on the result, so you can safely dounique_ptr<int[]> p(new int[42]);
for example.shared_ptr
you'd still need a custom deleter, but you wouldn't need a specialized shared or unique array pointer. Of course, such things are usually best replaced bystd::vector
anyway. Unfortunatelyshared_ptr
does not provide an array access function, so you'd still have to manually callget()
, butunique_ptr<T[]>
providesoperator[]
instead ofoperator*
andoperator->
. In any case, you have to bounds check yourself. This makesshared_ptr
slightly less user-friendly, although arguably the generic advantage and no Boost dependency makesunique_ptr
andshared_ptr
the winners again.Scoped pointers: Made irrelevant by
unique_ptr
, just likeauto_ptr
.There's really nothing more to it. In C++03 without move semantics this situation was very complicated, but in C++11 the advice is very simple.
There are still uses for other smart pointers, like
intrusive_ptr
orinterprocess_ptr
. However, they're very niche and completely unnecessary in the general case.何时使用
unique_ptr
的情况:何时使用
shared_ptr< /code>:
情况下共享对象 何时使用
weak_ptr
的情况:充当 编辑并添加更多
Cases of when to use
unique_ptr
:Cases of when to use
shared_ptr
:Cases of when to use
weak_ptr
:Feel free to edit and add more