好的,所以我最后一次写C++为生,EDOCX1,0,所有的STD LIB都是可用的,EDCOX1,1是愤怒的。我从未真正研究过Boost提供的其他智能指针类型。我知道C++ 11现在提供了一些类型的Boost,但不是全部。
那么,是否有人有一个简单的算法来决定何时使用哪个智能指针?最好包括关于哑指针(如T*)和其他boost智能指针的建议。(像这样的东西很好)。
- 另请参见std::auto_ptr to std::unique_ptr
- 我真的希望有人能想出一个很好的简单流程图,像这个STL选择流程图。
- @艾尔斯:哦,真是太好了!我把它传真了出来。
- @除尘器,甚至不接近是一个副本。链接的问题说"我什么时候应该使用智能指针",而这个问题是"我什么时候应该使用这些智能指针?"也就是说,这一个是对标准智能指针的不同用途进行分类。链接的问题不能做到这一点。差别似乎很小,但很大。
共享所有权:采用的shared_ptr和weak_ptr标准与它们的增强型标准基本相同。当你需要共享一个资源,不知道哪一个是最后一个活着的时候,就使用它们。使用weak_ptr观察共享资源,不影响其生命周期,不破坏循环。使用shared_ptr的周期通常不应该发生-两个资源不能相互拥有。
请注意,Boost还提供了shared_array,这可能是shared_ptr const>的一个合适的替代方案。
接下来,Boost提供了intrusive_ptr,这是一个轻量级的解决方案,如果您的资源已经提供了参考计数管理,并且您希望采用RAII原则。这个标准没有采用。
唯一所有权:Boost还有一个scoped_ptr,它是不可复制的,不能为它指定删除程序。std::unique_ptr是boost::scoped_ptr类固醇,当你需要一个智能指针时,它应该是你的默认选择。它允许您在模板参数中指定一个删除程序,并且可以移动,与boost::scoped_ptr不同。它也可以在STL容器中完全使用,只要您不使用需要可复制类型的操作(显然)。
再次注意,Boost有一个数组版本:scoped_array,标准统一要求std::unique_ptr部分专门化,将delete[]作为指针,而不是delete作为指针(使用default_deleter)。std::unique_ptr也提供operator[]而不是operator*和operator->。
请注意,std::auto_ptr仍在标准中,但已弃用。§D.10 [depr.auto.ptr]
The class template auto_ptr is deprecated. [ Note: The class template unique_ptr (20.7.1) provides a better solution. —end note ]
无所有权:当您知道资源将比引用对象/作用域长时,请使用哑指针(原始指针)或引用来表示对资源的非拥有引用。当需要可空性或可重设性时,首选引用并使用原始指针。
如果您想要一个对资源的非拥有引用,但您不知道该资源是否比引用它的对象寿命长,请将该资源打包到shared_ptr中,并使用weak_ptr—您可以测试父级shared_ptr是否与lock共存,如果该资源仍然存在,则返回一个非空的shared_ptr。如果要测试资源是否已死亡,请使用expired。这两个听起来可能很相似,但在同时执行时却截然不同,因为expired只保证它对单个语句的返回值。一个看似无辜的测试
1 2
| if(!wptr.expired())
something_assuming_the_resource_is_still_alive(); |
是一种潜在的竞争条件。
- 在没有所有权的情况下,您可能更喜欢对指针的引用,除非您不需要所有权和可重设性(在引用不会剪切指针的情况下),甚至您可能会考虑将原始对象重写为shared_ptr,而非所有权指针则是weak_ptr。
- @大卫:提到指针不是个好主意。如果它们不是通过实际拥有的ptr进行设置,而是通过函数的局部或参数进行设置,会怎么样?
- 我不是指指针,而是指引用而不是指针。如果没有所有权,除非您需要可重设性(或为空性,但不能够重设为空性会非常有限),否则首先可以使用一个简单的引用,而不是指针。
- @大卫:啊,我明白了。:)是的,推荐信也不错,在这种情况下,我个人也喜欢推荐信。我会加上它们。
- @Xeo:shared_array是shared_ptr的替代品,而不是shared_ptr>的替代品:它不能生长。
- @r.martinhofernandes:shared_ptr不专门用于数组,您需要提供自己的删除程序,它只需要shared_ptr即可。
- 好吧,我的意思是它的行为更像是这样一种假设的专门化(或一个普通的带有default_deleter的专门化)而不是shared_ptr>,我还加了一个注释,在weak_ptr中,你可以测试指针是与expired一起死的,还是与lock一起活的。如果对象是活动的(因此其名称),则expired不适合进行测试。
- @R.Martinhovernandes:meh,终于来更新这个答案了。修改为有关如何测试资源可用性的信息。
- 原始指针还允许手动垃圾收集。您可以使用release来获得唯一的指针,但这并不能释放内存。
- @ JohnJohn:EDCOX1(0)是用来破坏对象并释放EDCOX1(1)管理的内存的函数,"垃圾收集"意味着C++中不需要的很多东西(例如内存中的摸索)。release非常刻意地不释放内存,因为这是一种表示你想从unique_ptr中获得所有权的方式——如果release被破坏/取消,就没有任何东西可以拥有。
- 引用不能反弹,这在复制语义中可能有问题,因此您可以使用std::reference_wrapper。不幸的是,后者有点迟钝,因为目前不可能覆盖operator.。
- 值得注意的是,最后一个示例不是线程安全的。可能不是很多代码库的问题。
- @格雷戈罗科里:那是……我到底写了什么?我说这是一个潜在种族状况的例子。
- 对不起,伙计,我错过了。漫长的一天。
决定使用什么智能指针是一个所有权问题。在资源管理方面,如果对象A控制对象B的生存期,则它拥有对象B。例如,成员变量由其各自的对象拥有,因为成员变量的生存期与对象的生存期相关联。根据对象的拥有方式选择智能指针。
请注意,软件系统中的所有权与所有权是分开的,正如我们在软件之外所想的那样。例如,一个人可能"拥有"自己的家,但这并不一定意味着一个Person对象在House对象的生命周期内拥有控制权。将这些现实世界的概念与软件概念相结合,无疑是一种让自己陷入困境的方法。
如果您拥有该物品的唯一所有权,请使用std::unique_ptr。
如果您共享了对象的所有权…-如果没有所有权循环,则使用std::shared_ptr。-如果有循环,定义一个"方向",在一个方向上使用std::shared_ptr,在另一个方向上使用std::weak_ptr。
如果对象拥有您,但可能没有所有者,请使用普通指针T*(例如父指针)。
如果对象拥有您(或以其他方式保证存在),请使用引用T&。
警告:注意智能指针的成本。在内存或性能有限的环境中,只使用普通指针和更为手动的内存管理方案是有益的。
成本:
- 如果您有一个自定义的删除程序(例如,您使用分配池),那么每个指针的开销都会很容易被手动删除所避免。
- std::shared_ptr在复制时有一个引用计数增量的开销,加上一个对销毁的减量,然后是一个0计数检查,删除保留的对象。根据实现的不同,这可能会使代码膨胀并导致性能问题。
- 编译时间。与所有模板一样,智能指针对编译时间也有负面影响。
实例:
1 2 3 4 5
| struct BinaryTree
{
Tree* m_parent;
std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
}; |
二叉树不拥有其父树,但树的存在意味着其父树的存在(或根目录的nullptr),因此使用普通指针。二叉树(具有值语义)拥有其子代的唯一所有权,因此这些子代是std::unique_ptr。
1 2 3 4 5
| struct ListNode
{
std::shared_ptr<ListNode> m_next;
std::weak_ptr<ListNode> m_prev;
}; |
在这里,list节点拥有其下一个和上一个列表,因此我们定义了一个方向,并使用shared_ptr表示下一个,使用weak_ptr表示prev来中断循环。
- 对于二叉树的例子,有些人会建议对孩子使用shared_ptr,对父母关系使用weak_ptr。
- @davidrodr&237;guez dribeas:这取决于树是否具有值语义。如果人们打算从外部引用您的树,即使源树被破坏,那么是的,共享/弱指针组合将是最好的选择。
- 如果一个对象拥有您并且保证存在,那么为什么不引用它呢?
- @Lokiastari:可能为空。我再补充一下。
- 如果使用引用,则不能更改父级,这可能会妨碍或不妨碍设计。为了平衡树木,这会妨碍。
- @Moingduck,我真的有一种方法来重新绑定引用,就因为这个原因。
- @deft-code:std::reference-wrapper有一种方法,如果我还记得的话。
- +1但是您应该在第一行添加"所有权"的定义。我经常发现自己必须清楚地说明,这是关于对象的生死,而不是更具体领域意义上的所有权。
- @克拉姆:好评论。我在顶部添加了一个定义。干杯。
- 这是一个很好的解释,但我不能理解两件事:1:弱ptr有什么不同;2:这里的循环问题是什么?你能给我一些动机问题吗?如struct ListNode { std::shared_ptr m_next; std::shared_ptr m_prev; // no weak ptr , now what? };。
- @soulreaper:循环是一个问题,因为shared_ptr使用引用计数,如果两个对象相互引用,那么它们将永远不会减少引用计数。
- "如果物品拥有你"-我不明白这是什么意思,你能解释一下吗,请?
一直使用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提供operator[],而不是operator*和operator->。在任何情况下,你都必须自己检查边界。这使得shared_ptr的用户友好性稍差,尽管可以说,通用优势和无增强依赖性使unique_ptr和shared_ptr再次成为赢家。
作用域指针:被unique_ptr置为无关,就像auto_ptr一样。
真的没有别的了。在没有移动语义的C++ 03中,这种情况非常复杂,但是在C++ 11中,建议非常简单。
还有其他智能指针的用途,如intrusive_ptr或interprocess_ptr。然而,在一般情况下,它们是非常利基和完全不必要的。
- 另外,用于迭代的原始指针。对于输出参数缓冲区,缓冲区归调用方所有。
- @我已经编辑过了。
- 嗯,我读到的,是协变返回和非拥有的情况。如果你的意思是联合而不是交集,重写可能会很好。我还想说,迭代也值得特别提及。
- std::unique_ptr提供operator[]而不是operator*和operator->。当然,你还是需要做约束检查。
何时使用unique_ptr的情况:
- 工厂方法
- 作为指针的成员(包括PIMPL)
- 在stl contaters中存储指针(以避免移动)
- 使用大型局部动态对象
何时使用shared_ptr的情况:
何时使用weak_ptr的情况:
- 用作一般引用的大映射(例如,所有打开的套接字的映射)
随意编辑和添加更多内容