智能指针(提升)说明


220

以下一组指针有什么区别?何时在生产代码中使用每个指针(如果有的话)?

例子将不胜感激!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

您是否在生产代码中使用boost?

Answers:


339

智能指针的基本属性

当您具有可以分配每个智能指针的属性时,这很容易。有三个重要属性。

  • 完全没有所有权
  • 所有权转移
  • 所有权份额

第一种意味着智能指针不能删除对象,因为它不拥有它。第二个意味着仅一个智能指针可以同时指向同一对象。例如,如果要从函数返回智能指针,则将所有权转移到返回的智能指针。

第三种意味着多个智能指针可以同时指向同一对象。这也适用于原始指针,但是原始指针缺乏重要的功能:它们不定义它们是否拥有。如果每个所有者放弃对象,则所有权共享智能指针将删除该对象。经常需要执行此操作,因此共享拥有的智能指针得到了广泛传播。

一些拥有的智能指针既不支持第二个也不支持第三个。因此,它们不能从函数返回或传递到其他地方。这最适合于RAII将智能指针保留在本地且仅在创建后将其移出范围后释放对象的目的。

所有权共享可以通过使用副本构造函数来实现。这自然会复制一个智能指针,并且副本和原始对象都将引用同一对象。所有权转移目前无法真正在C ++中实现,因为没有办法将某种东西从一个对象转移到该语言支持的另一个对象:如果尝试从函数返回一个对象,那么实际上是在复制对象。因此,实现所有权转移的智能指针必须使用复制构造函数来实现所有权转移。但是,这又反过来破坏了其在容器中的用法,因为需求说明了容器元素的复制构造函数的某种行为,该行为与这些智能指针的所谓“移动构造函数”行为不兼容。

C ++ 1x通过引入所谓的“移动构造函数”和“移动赋值运算符”,为所有权转让提供了本机支持。它还带有称为的所有权转移智能指针unique_ptr

分类智能指针

scoped_ptr是既不可转让也不可共享的智能指针。如果您在本地需要分配内存,它只是可用的,但是请确保在超出范围时再次将其释放。但是,如果您愿意,它仍然可以与另一个scoped_ptr交换。

shared_ptr是共享所有权(以上第三种)的智能指针。它被引用计数,因此它可以查看其最后一个副本何时超出范围,然后释放所管理的对象。

weak_ptr是一个没有所有权的智能指针。它用于引用受管理对象(由shared_ptr管理)而无需添加引用计数。通常,您需要从shared_ptr中获取原始指针并进行复制。但这并不安全,因为您将无法检查实际删除对象的时间。因此,weak_ptr通过引用shared_ptr管理的对象来提供方法。如果您需要访问该对象,则可以锁定它的管理(以避免在另一个线程中,shared_ptr在使用该对象时将其释放),然后再使用它。如果weak_ptr指向已删除的对象,它将抛出异常来通知您。当您有循环引用时,使用weak_ptr最为有益:引用计数无法轻松应对这种情况。

intrusive_ptr就像shared_ptr一样,但是它不会将引用计数保留在shared_ptr中,而是将计数的增加/减少留给了一些需要由被管理对象定义的辅助函数。这样做的好处是可以将已经被引用的对象(其引用计数由外部引用计数机制递增)可以填充到intrusive_ptr中-因为该引用计数不再位于智能指针的内部,而是智能指针使用现有的参考计数机制。

unique_ptr是所有权指针的转移。您不能复制它,但是可以使用C ++ 1x的move构造函数移动它:

unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!

这是std :: auto_ptr所遵循的语义,但是由于缺少对移动的本机支持,因此无法为它们提供陷阱。unique_ptr会自动从临时的其他unique_ptr中窃取资源,这是移动语义的关键特征之一。auto_ptr在下一个C ++ Standard版本中将不推荐使用,而推荐使用unique_ptr。C ++ 1x还允许填充只能移动但不能复制到容器中的对象。因此,您可以将unique_ptr填充到一个向量中。如果您想了解更多有关此内容的信息,我将在这里停止,并为您提供一篇不错的文章


3
感谢您的赞美。我很感激,所以您现在也将获得+1:p
Johannes Schaub-litb 2009年

@litb:我对“所有权转移”有疑问;我确实同意在C ++ 03中没有在对象之间进行真正的所有权转移,但是对于智能指针而言,不能通过此处所述的破坏性复制机制来做到这一点notifyit.com/articles/article.aspx?p=31529&seqNum= 5
legends2k 2010年

3
很棒的答案。注意:auto_ptr已弃用(C ++ 11)。
nickolay 2012年

2
“这反过来又破坏了它在容器中的使用,因为需求说明了容器元素的复制构造函数的某种行为,这与这些智能指针的所谓“移动构造函数”行为不兼容。” 没有得到那部分。
Raja

我还被告知,intrusive_ptrshared_ptr更好的缓存一致性相比,它更可取。显然,如果将引用计数存储为托管对象本身而不是单独对象的一部分,则缓存的性能会更好。这可以在被管理对象的模板或超类中实现。
艾略特(Eliot)

91

scoped_ptr是最简单的。当超出范围时,它将被销毁。以下代码是非法的(scoped_ptrs是不可复制的),但将说明一点:

std::vector< scoped_ptr<T> > tPtrVec;
{
     scoped_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory

shared_ptr被引用计数。每次进行复制或分配时,参考计数都会增加。每次触发实例的析构函数时,原始T *的引用计数都会减少。一旦为0,则释放指针。

std::vector< shared_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     // This copy to tPtrVec.push_back and ultimately to the vector storage
     // causes the reference count to go from 1->2
     tPtrVec.push_back(tPtr);
     // num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe

weak_ptr是对共享指针的弱引用,它要求您检查指向的shared_ptr是否仍然存在

std::vector< weak_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed =  tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
     cout << "Raw T* was freed, can't access it"
}
else
{
     tPtrVec[0]->DoSomething(); // raw 
}

intrusive_ptr通常在必须使用第三者智能ptr时使用。它将调用一个免费函数来增加和减少引用计数。有关更多信息,请参阅boost文档的链接


心不是if (tPtrAccessed[0].get() == 0)假设是if (tPtrAccessed.get() == 0)
Rajeshwar

@DougT。您是否相信Java在References中使用相同的想法?软,硬,弱等?
gansub

20

boost::ptr_container在任何有关增强智能指针的调查中都不要忽略。在例如std::vector<boost::shared_ptr<T> >太慢的情况下,它们可能是无价的。


实际上,我上次尝试时,基准测试表明,自从我最初编写此代码以来,至少在典型的PC硬件上,性能差距已大大缩小!不过,在小众用例中,更有效的ptr_container方法可能仍具有一些优势。
2016年

12

我赞同有关查看文档的建议。它并没有看起来那么可怕。和一些简短的提示:

  • scoped_ptr-指针超出范围时会自动删除。注意-无法分配,但不会产生开销
  • intrusive_ptr-引用计数指针,无开销smart_ptr。但是对象本身存储引用计数
  • weak_ptr-与shared_ptr一起处理导致循环依赖的情况(请阅读文档,并在google上搜索漂亮的图片;)
  • shared_ptr -通用,最强大(最重量级)的智能指针(来自boost提供的指针)
  • 还有一个old auto_ptr,可确保当控件离开作用域时,它指向的对象会自动销毁。但是,它具有与其他人不同的复制语义。
  • unique_ptr- 将随C ++ 0x一起提供

回复编辑:


8
我来这里是因为我发现Boost文档太吓人了。
弗朗索瓦·博塔2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.