好的,所以我上一次写C ++为生时,std::auto_ptr
所有的std lib都可用,而且boost::shared_ptr
风靡一时。我从来没有真正研究过提供的其他智能指针类型。我知道C ++ 11现在提供了某些类型的boost,但不是全部。
那么,有人可以通过简单的算法来确定何时使用哪个智能指针吗?最好包括有关哑指针(原始指针,如T*
)和其他Boost智能指针的建议。(喜欢的东西这将是巨大的)。
好的,所以我上一次写C ++为生时,std::auto_ptr
所有的std lib都可用,而且boost::shared_ptr
风靡一时。我从来没有真正研究过提供的其他智能指针类型。我知道C ++ 11现在提供了某些类型的boost,但不是全部。
那么,有人可以通过简单的算法来确定何时使用哪个智能指针吗?最好包括有关哑指针(原始指针,如T*
)和其他Boost智能指针的建议。(喜欢的东西这将是巨大的)。
Answers:
共享所有权:
在shared_ptr
和weak_ptr
采用的标准是几乎一样的升压同行。当您需要共享资源并且不知道哪个资源将成为最后一个存在时,请使用它们。使用weak_ptr
观察共享资源而不影响其寿命,不打破循环。shared_ptr
通常不应发生周期-两个资源不能互相拥有。
请注意,Boost还提供shared_array
,可能是的合适替代shared_ptr<std::vector<T> const>
。
接下来,Boost提供intrusive_ptr
,如果您的资源已经提供了引用计数的管理并且您希望将其应用于RAII原则,则这是一个轻量级的解决方案。该标准未被标准采用。
唯一所有权:
Boost也有一个scoped_ptr
,它不可复制,并且您不能为其指定删除器。std::unique_ptr
是boost::scoped_ptr
类固醇,应该是你当你需要一个智能指针默认选择。它允许您在其模板参数中指定一个deleteer,并且可以移动,而不是boost::scoped_ptr
。只要您不使用需要可复制类型的操作(显然),它在STL容器中也可以完全使用。
再次注意,Boost具有一个数组版本:scoped_array
,该标准通过要求std::unique_ptr<T[]>
部分特殊化来统一该标准,该特殊部分将delete[]
使用指针而不是对指针进行delete
赋值(使用default_delete
r)。std::unique_ptr<T[]>
还提供operator[]
代替operator*
和operator->
。
请注意,std::auto_ptr
它仍然在标准中,但已弃用。
§D.10 [depr.auto.ptr]
类模板
auto_ptr
已弃用。[ 注意:类模板unique_ptr
(20.7.1)提供了更好的解决方案。—尾注 ]
无所有权:对资源的非所有权引用
使用哑指针(原始指针)或引用,并且当您知道资源将超过引用对象/作用域时。当您需要可空性或可重置性时,请首选引用并使用原始指针。
如果你想有一个非所属参照的资源,但你不知道,如果资源将活得比的对象引用它,包装资源的shared_ptr
和使用weak_ptr
-你可以测试,如果家长shared_ptr
是活着的lock
,这将shared_ptr
如果资源仍然存在,则返回非空值。如果要测试资源是否已耗尽,请使用expired
。两者听起来很相似,但面对并发执行却有很大不同,因为它们expired
只能保证单个语句的返回值。看似无辜的考验
if(!wptr.expired())
something_assuming_the_resource_is_still_alive();
是潜在的比赛条件。
shared_ptr
,将非所有权指针重写为一个weak_ptr
...
shared_array<T>
是另一种shared_ptr<T[]>
不shared_ptr<vector<T>>
:它无法生长。
决定使用哪种智能指针是所有权的问题。在资源管理方面,如果对象A 控制着对象B的生存期,则它拥有对象B。例如,成员变量归其各自的对象所有,因为成员变量的生存期与对象的生存期相关。您可以根据对象的拥有方式来选择智能指针。
请注意,软件系统中的所有权与所有权是分开的,正如我们在软件外部所认为的那样。例如,一个人可能“拥有”自己的房屋,但这并不一定意味着某个Person
对象可以控制该对象的寿命House
。将这些现实世界的概念与软件概念相结合,是一种使自己陷入困境的可靠方法。
如果您拥有该对象的唯一所有权,请使用std::unique_ptr<T>
。
如果您拥有对象的共享所有权...-
如果所有权中没有循环,请使用std::shared_ptr<T>
。
-如果存在循环,则定义“方向”,并std::shared_ptr<T>
在一个方向和另一个方向上使用std::weak_ptr<T>
。
如果对象拥有您,但有可能没有所有者,请使用普通指针T*
(例如,父指针)。
如果对象拥有您(或以其他方式保证存在),请使用references T&
。
注意:请注意智能指针的成本。在内存或性能受限的环境中,仅将普通指针与更手动的方案一起使用来管理内存可能是有益的。
费用:
std::shared_ptr
开销是复制时引用计数增加,加上销毁减少,然后进行0计数检查并删除保留的对象。根据实现的不同,这可能会使您的代码膨胀,并导致性能问题。例子:
struct BinaryTree
{
Tree* m_parent;
std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};
二叉树不拥有其父级,但是一棵树的存在意味着其父级(或nullptr
根目录)的存在,因此使用普通指针。一棵二叉树(具有值语义)对其子节点拥有唯一所有权,因此它们是std::unique_ptr
。
struct ListNode
{
std::shared_ptr<ListNode> m_next;
std::weak_ptr<ListNode> m_prev;
};
在这里,列表节点拥有其下一个和上一个列表,因此我们定义一个方向,并shared_ptr
用于下一个和上一个weak_ptr
打破循环。
shared_ptr<BinaryTree>
用于子级和weak_ptr<BinaryTree>
父级关系。
unique_ptr<T>
除非需要参考计数,否则请一直使用shared_ptr<T>
(在这种情况下)(在极少数情况下,weak_ptr<T>
应避免参考周期)。在几乎每种情况下,可转让的唯一所有权都很好。
原始指针:仅在需要协变返回(可能发生的非所有权指向)时才有用。否则,它们就没有什么用了。
数组指针:unique_ptr
具有专门的属性,T[]
可以自动调用delete[]
结果,因此可以放心地进行操作unique_ptr<int[]> p(new int[42]);
。shared_ptr
您仍然需要自定义删除器,但不需要专门的共享或唯一数组指针。当然,std::vector
无论如何,通常最好用这种东西来代替。不幸的是,shared_ptr
它没有提供数组访问功能,因此您仍然必须手动调用get()
,而是unique_ptr<T[]>
提供了operator[]
代替operator*
和的功能operator->
。无论如何,您都必须检查自己。shared_ptr
尽管可以说这是通用优势,并且没有Boost依赖关系,但最终unique_ptr
还是shared_ptr
使赢家再次出现,这使得用户友好度略有降低。
范围指针:与无关unique_ptr
,就像auto_ptr
。
真的没有更多了。在没有移动语义的C ++ 03中,这种情况非常复杂,但是在C ++ 11中,建议非常简单。
其他智能指针(如intrusive_ptr
或)仍然有用途interprocess_ptr
。但是,它们非常小众,在一般情况下完全没有必要。
std::unique_ptr<T[]>
提供operator[]
代替operator*
和operator->
。的确,您仍然需要对自己进行边界检查。