230

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

示例将不胜感激!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

你在生产代码中使用 boost 吗?

4

4 回答 4

350

智能指针的基本属性

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

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

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

第三个意思是多个智能指针可以同时指向同一个对象。这也适用于原始指针,但是原始指针缺少一个重要特性:它们不定义它们是否拥有。如果每个所有者都放弃该对象,则所有权共享智能指针将删除该对象。这种行为恰好经常需要,因此共享拥有智能指针被广泛传播。

一些拥有智能指针既不支持第二个也不支持第三个。因此它们不能从函数返回或传递到其他地方。这最适合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 的移动构造函数来移动它:

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++ 标准版本中被弃用,取而代之的是 unique_ptr。C++1x 还将允许将只能移动但不能复制到容器中的对象填充。因此,例如,您可以将 unique_ptr 填充到向量中。如果您想了解更多关于此的信息,我将在这里停下来,并为您提供一篇关于此的精彩文章。

于 2009-02-20T15:19:34.980 回答
93

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通常在您必须使用第 3 方智能 ptr 时使用。它将调用一个免费函数来添加和减少引用计数。有关更多信息,请参阅 boost 文档的链接。

于 2009-02-20T14:51:22.253 回答
20

不要忽视boost::ptr_container任何关于提升智能指针的调查。std::vector<boost::shared_ptr<T> >在例如太慢的情况下,它们可能是无价的。

于 2009-02-20T15:06:34.510 回答
12

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

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

对编辑的回应:

于 2009-02-20T14:59:58.957 回答