聪明的指针:或谁拥有你的宝贝?

时间:2008-09-18 16:35:17

标签: c++ memory-management smart-pointers ownership-semantics

C ++完全是关于内存所有权的 Aka“所有权语义

动态分配的内存块的所有者有责任释放该内存。所以这个问题真正成为谁拥有记忆。

在C ++中,所有权都是由RAW指针包含在其中的类型记录的,因此在一个好的(IMO)C ++程序中,很少见[RARE并非绝对]看到RAW指针传递(因为RAW指针没有推断的所有权因此我们无法分辨谁拥有记忆,因此如果没有仔细阅读文件,你无法分辨谁负责所有权。

相反,很少看到RAW指针存储在类中,每个RAW指针都存储在自己的SMART指针包装器中。 ( N.B。:如果您没有对象,则不应该存储它,因为您无法知道它什么时候会超出范围并被销毁。)

所以问题是:

  • 人们遇到什么类型的所有权语义?
  • 使用哪些标准类来实现这些语义?
  • 您认为它们在哪些情况下有用?

让每个答案保留1种语义所有权,以便可以单独上下投票

要点:

从概念上讲,智能指针很简单,而且简单易用。我已经看过许多尝试过的实现,但总是以某种方式打破它们,这对于随意使用和示例来说并不明显。因此,我建议始终使用经过良好测试的“智能指针”,而不是自己动手。 std :: auto_ptr或其中一个提升智能指针似乎可以满足我的所有需求。

的std :: auto_ptr的< T>:

单身人士拥有该物品    但允许转让所有权。

用法:
   ======
   这允许您定义显示所有权显式转移的接口。

升压:: scoped_ptr的< T>

单身人士拥有该物品    不允许转让所有权。

用法:
   ======
   用于显示明确的所有权    对象将被析构函数或明确重置时销毁。

升压:: shared_ptr的< T> (标准:: TR1 :: shared_ptr的< T&GT)

多重所有权。
   这是一个简单的引用计数指针。当引用计数达到零时,对象被销毁。

用法:
   ======
   当对象可以有多个拥有者,其生命周期无法在编译时确定。

升压::的weak_ptr< T>

与shared_ptr< T>一起使用    在可能发生指针循环的情况下。

用法:
   ======
   用于在仅循环维持共享引用计数时停止保留对象的周期。

11 个答案:

答案 0 :(得分:23)

简单C ++模型

在我看到的大多数模块中,默认情况下,假设接收指针接收所有权。事实上,放弃指针所有权的函数/方法都非常罕见,并在文档中明确表达了这一事实。

此模型假定用户仅是他/她明确分配的所有者。其他所有内容都会自动处理(在范围退出处或通过RAII)。这是一个类似C的模型,扩展的事实是大多数指针都由对象拥有,这些对象将自动或在需要时释放它们(主要是在所述对象破坏时),并且对象的生命持续时间是可预测的(RAII是你的朋友,再次)。

在这个模型中,原始指针是自由流通的,并且通常没有危险(但如果开发人员足够聪明,他/她将尽可能使用引用)。

  • 原始指针
  • 的std :: auto_ptr的
  • 升压:: scoped_ptr的

智能指向C ++模型

在一个充满智能指针的代码中,用户可以希望忽略对象的生命周期。所有者永远不是用户代码:它是智能指针本身(RAII,再次)。 问题是循环引用与引用计数智能指针混合可能致命,因此您必须同时处理共享指针和弱指针。所以你仍然需要考虑所有权(弱指针很可能没有任何意义,即使它优于原始指针的优点是它可以告诉你)。

  • 升压:: shared_ptr的
  • 升压::的weak_ptr

结论

无论我描述的模型如何,除非异常,接收指针接收其所有权,知道谁拥有谁<仍然非常重要< / strong>即可。即使对于大量使用引用和/或智能指针的C ++代码也是如此。

答案 1 :(得分:20)

对我来说,这3种可以满足我的大部分需求:

shared_ptr - 当计数器达到零时,引用计数,解除分配

weak_ptr - 与上述相同,但它是shared_ptr的“奴隶”,无法取消分配

auto_ptr - 当创建和释放发生在同一个函数内部时,或者当对象必须被认为只有一个所有者时。当你将一个指针分配给另一个指针时,第二个指针会从第一个指定“窃取”该对象。

我有自己的实现,但它们也可以在Boost中找到。

我仍然通过引用传递对象(尽可能const),在这种情况下,被调用的方法必须假定对象仅在调用期间处于活动状态。

我使用了另一种指针,我称之为 hub_ptr 。当你拥有一个必须可以从嵌套在其中的对象访问的对象时(通常作为一个虚拟基类)。这可以通过将weak_ptr传递给他们来解决,但它本身没有shared_ptr。因为它知道这些对象不会比他长寿,所以它会将hub_ptr传递给它们(它只是常规指针的模板包装器)。

答案 2 :(得分:10)

没有共享所有权。如果您这样做,请确保它只包含您无法控制的代码。

这解决了100%的问题,因为它迫使你理解一切如何相互作用。

答案 3 :(得分:2)

  • 共享所有权
  • 升压:: shared_ptr的

在多个对象之间共享资源时。 boost shared_ptr使用引用计数来确保在每个人都被finsihed时解除分配资源。

答案 4 :(得分:2)

std::tr1::shared_ptr<Blah>通常是您最好的选择。

答案 5 :(得分:2)

从提升开始,还有pointer container库。如果你只是在容器的上下文中使用对象,这些比智能指针的标准容器更有效,更容易使用。

在Windows上,有COM指针(IUnknown,IDispatch和朋友),以及用于处理它们的各种智能指针(例如ATL的CComPtr和由“import”语句自动生成的智能指针。 Visual Studio基于_com_ptr类)。

答案 6 :(得分:1)

  • 一位所有者
  • 升压:: scoped_ptr的

当你需要动态分配内存但又想确保它在块的每个出口点都被释放时。

我发现这很有用,因为它可以很容易地重新安装,并且无需担心泄漏即可发布

答案 7 :(得分:1)

我认为自己无法在设计中拥有共享权。事实上,从我的头脑中,我能想到的唯一有效的案例是Flyweight模式。

答案 8 :(得分:1)

yasper :: ptr是一个轻量级的boost :: shared_ptr之类的替代品。它适用于我(现在)的小项目。

http://yasper.sourceforge.net/的网页中,描述如下:

  

为什么要编写另一个C ++智能指针?   已经存在几个高点   优质的智能指针实现   对于C ++,最突出的是Boost   指针万神殿和Loki的SmartPtr。   为了比较智能指针   实现以及何时使用它们   适当的请阅读Herb Sutter's   新C ++:智能(呃)指针。在   与广阔的功能形成鲜明对比   其他图书馆,Yasper是一个   狭隘的重点参考计数   指针。它与之密切相关   Boost的shared_ptr和Loki的   RefCounted / AllowConversion策略。   Yasper允许C ++程序员使用   忘记没有内存管理   介绍Boost的大型   依赖或不得不了解   Loki的复杂政策模板。   理念

* small (contained in single header)
* simple (nothing fancy in the code, easy to understand)
* maximum compatibility (drop in replacement for dumb pointers)
     

最后一点可能很危险,因为   yasper允许冒险(但有用)   动作(例如分配给原始动作   指针和手动释放)   其他实现不允许。   小心,只有使用这些功能   你知道你在做什么!

答案 9 :(得分:1)

还有另一种经常使用的单一可转让所有者形式,它优于auto_ptr,因为它避免了auto_ptr分配语义疯狂腐败所引起的问题。

我说的只是swap。任何具有合适swap功能的类型都可以被视为某些内容的智能引用,直到将所有权转移到同一类型的另一个实例,通过交换为止他们。每个实例都保留其标识,但绑定到新内容。这就像是一个安全可重新绑定的参考。

(这是一个智能参考而不是智能指针,因为您不必明确取消引用它来获取内容。)

这意味着auto_ptr变得不那么必要了 - 只需要填补类型没有良好swap功能的空白。但是所有std容器都可以。

答案 10 :(得分:0)

  • 一位所有者:Aka删除复制
  • 的std :: auto_ptr的

当对象的创建者想要明确地将所有权交给其他人时。 这也是我给你的代码记录的方式,我不再跟踪它,所以一定要在完成后删除它。