我什么时候使用哪种指针?

sbi*_*sbi 224 c++ pointers smart-pointers c++-faq c++11

好吧,所以我最后一次以C++为生,std::auto_ptr所有的std lib都可用,而且boost::shared_ptr风靡一时.我从未真正研究过提供的其他智能指针类型.我知道C++ 11现在提供了一些类型的提升,但不是全部.

那么有人有一个简单的算法来确定何时使用哪个智能指针?优选地包括关于哑指针(诸如原始指针T*)和其他增强智能指针的建议.(像这样的东西会很棒).

Xeo*_*Xeo 178

共享所有权:
采用的shared_ptrweak_ptr标准与Boost对应的几乎相同.当您需要共享资源并且不知道哪个资源将是最后一个存活时使用它们.使用weak_ptr观察共享资源而不影响其寿命,不打破循环.shared_ptr通常不应该发生循环- 两个资源不能互相拥有.

请注意,Boost另外提供shared_array,可能是一个合适的替代品shared_ptr<std::vector<T> const>.

接下来,Boost提供intrusive_ptr了一个轻量级的解决方案,如果您的资源已经提供了参考计数管理,并且您希望将其用于RAII原则.标准没有采用这个.

独特的所有权:
Boost还有一个scoped_ptr不可复制的,您无法指定删除者.std::unique_ptrboost::scoped_ptr类固醇,当你需要一个智能指针时应该是你的默认选择.它允许您在其模板参数中指定删除器,并且可以移动,不像boost::scoped_ptr.只要您不使用需要可复制类型的操作(显然),它也可以在STL容器中完全使用.

再次注意,Boost有一个数组版本:scoped_array,标准统一通过要求std::unique_ptr<T[]>部分特化delete[]而不是指针delete(用default_deleter).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如果资源仍然存在,则返回非null值.如果要测试资源是否已死,请使用expired.这两者可能听起来很相似,但在并发执行方面却非常不同,因为expired只保证它对该单个语句的返回值.一个看似无辜的测试

if(!wptr.expired())
  something_assuming_the_resource_is_still_alive();
Run Code Online (Sandbox Code Playgroud)

是一种潜在的竞争条件.

  • 我并不是指*引用指针*,而是引用*而不是*指针.如果没有所有权,除非您需要可重置性(或可空性,但无法重置的可空性将非常有限),您可以首先使用普通引用而不是指针. (2认同)
  • @GregroyCurrie:这就是我写的?我说过这是潜在竞争条件的一个例子。 (2认同)

Pet*_*der 127

决定使用什么智能指针是一个所有权问题.对于资源管理,如果对象A 控制对象B的生命周期,则对象A 拥有对象B.例如,成员变量由其各自的对象拥有,因为成员变量的生命周期与对象的生命周期相关联.您可以根据对象的拥有方式选择智能指针.

请注意,软件系统中的所有权与所有权是分开的,正如我们在软件之外所想到的那样.例如,一个人可能"拥有"他们的家,但这并不一定意味着一个Person对象可以控制一个对象的生命周期House.将这些真实世界的概念与软件概念相结合是将自己编入漏洞的可靠方法.


如果您拥有该对象的唯一所有权,请使用std::unique_ptr<T>.

如果您拥有该对象的所有权...
- 如果所有权中没有周期,请使用std::shared_ptr<T>.
- 如果存在循环,则定义"方向"并std::shared_ptr<T>在一个方向和另一个方向上使用std::weak_ptr<T>.

如果对象拥有你,但有可能没有所有者,请使用普通指针T*(例如父指针).

如果对象拥有您(或以其他方式保证存在),请使用引用T&.


警告:注意智能指针的成本.在内存或性能受限的环境中,仅使用具有更多手动方案的普通指针来管理内存可能是有益的.

费用:

  • 如果您有自定义删除程序(例如,您使用分配池),那么这将导致每个指针的开销,这可以通过手动删除轻松避免.
  • std::shared_ptr具有复制时引用计数增量的开销,加上销毁时的减量,然后是删除保持对象的0计数检查.根据实现情况,这会使代码膨胀并导致性能问题.
  • 编译时间.与所有模板一样,智能指针对编译时间有负面影响.

例子:

struct BinaryTree
{
    Tree* m_parent;
    std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};
Run Code Online (Sandbox Code Playgroud)

二叉树不拥有其父级,但树的存在意味着其父级(或nullptr根)的存在,因此使用普通指针.二叉树(具有值语义)拥有其子项的唯一所有权,因此它们是std::unique_ptr.

struct ListNode
{
    std::shared_ptr<ListNode> m_next;
    std::weak_ptr<ListNode> m_prev;
};
Run Code Online (Sandbox Code Playgroud)

这里,列表节点拥有其下一个和前一个列表,因此我们定义一个方向并shared_ptr用于next和weak_ptrprev来打破循环.

  • 对于二叉树的例子,有些人建议对子节点使用`shared_ptr <BinaryTree>`,对父节点使用`weak_ptr <BinaryTree>`. (3认同)
  • +1但你应该在第一行添加"所有权"的定义.我经常发现自己必须清楚地说明它是关于物体的生死,而不是更具特定领域意义的所有权. (3认同)

Pup*_*ppy 19

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再次获胜者.

Scoped指针:无关紧要unique_ptr,就像auto_ptr.

真的没什么了不起的.在没有移动语义的C++ 03中,这种情况非常复杂,但在C++ 11中,建议非常简单.

还有其他智能指针的用途,比如intrusive_ptrinterprocess_ptr.然而,在一般情况下,它们非常适合并且完全没有必要.

  • `std :: unique_ptr <T []>`提供`operator []`而不是`operator*`和`operator->`.确实,你仍然需要自己进行绑定检查. (2认同)

Lal*_*and 7

何时使用的案例unique_ptr:

  • 工厂方法
  • 指针成员(包括pimpl)
  • 在stl包含器中存储指针(以避免移动)
  • 使用大型本地动态对象

何时使用的案例shared_ptr:

  • 跨线程共享对象
  • 一般共享对象

何时使用的案例weak_ptr:

  • 作为一般参考的大型地图(例如所有打开的套接字的地图)

随意编辑和添加更多

  • 事实上,当你给出场景时,我更喜欢你的答案。 (2认同)