关于c ++:智能指针:或者谁拥有你宝贝?

Smart Pointers: Or who owns you baby?

C++是关于内存所有权的。又称"所有权语义"

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

在C++的所有权中,A型原始指针被封装在一个好的(IMO)C++程序中,这是非常罕见的[难得的]看到原始指针的传递(因为原始指针没有推断的所有权,因此我们不能告诉谁拥有内存,因此没有仔细阅读文档,你就不能告诉谁负责。或所有权。

相反,很少看到原始指针存储在一个类中,每个原始指针都存储在它自己的智能指针包装器中。(N.B.:如果你不拥有一个对象,你就不应该存储它,因为你不知道它什么时候会超出范围并被破坏。)

所以问题是:

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

让我们为每个答案保留1种语义所有权,这样就可以分别对其进行上下投票。

总结:

概念上的智能指针很简单,简单的实现也很容易。我见过许多尝试过的实现,但它们总是以某种方式被破坏,这对于临时使用和示例来说并不明显。因此,我建议始终使用库中经过良好测试的"智能指针",而不是滚动自己的指针。自动指针或一个增强智能指针似乎可以满足我的所有需求。

std::auto-ptr

对象归个人所有。但是所有权的转移是允许的。

用途:不等于这允许您定义显示所有权显式转移的接口。

增强::作用域指针

对象归个人所有。不允许所有权转让。

用途:不等于用于显示明确的所有权。当显式重置时,析构函数或将销毁对象。

boost::shared_ptr(std::tr1::shared_ptr)

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

用途:不等于当对象可以有多个具有在编译时无法确定的生存期的欠函数时。

增强::弱指针

与共享的_ptr一起使用。在可能发生指针循环的情况下。

用途:不等于用于在仅循环维护共享引用计数时停止循环以保留对象。


简单C++模型

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

这个模型假定用户只是他/她显式分配的内容的所有者。其他的一切都会自动处理(在作用域出口处,或者通过raii)。这是一个类似C的模型,扩展了这样一个事实:大多数指针都由对象拥有,这些对象将自动或在需要时(主要是在所述对象销毁时)释放指针,并且对象的生命周期是可预测的(RAII也是您的朋友)。

在这个模型中,原始指针是自由循环的,并且大多数都不危险(但是如果开发人员足够聪明,他/她将尽可能使用引用)。

  • 裸指针
  • STD:AutoTypTR
  • 作用域指针

智能尖头C++模型

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

  • 共享资源
  • Boost::

结论

不管我描述的模型是什么,除非有例外,否则接收指针并不能接收到它的所有权,而且知道谁拥有谁仍然是非常重要的。即使是C++代码,大量使用引用和/或智能指针。


对我来说,这3种类型涵盖了我的大部分需求:

shared_ptr—参考计数,计数器为零时解除分配

weak_ptr—同上,但它是shared_ptr的"奴隶",不能解除分配

auto_ptr—当创建和解除分配发生在同一个函数中,或者当对象必须被视为只有一个所有者时。当您将一个指针分配给另一个指针时,第二个指针会从第一个指针"窃取"对象。

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

我仍然通过引用传递对象(只要可能,const),在这种情况下,被调用的方法必须假定对象只有在调用期间才是活动的。

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


没有共享所有权。如果你这样做了,确保只有你不控制的代码。

这就解决了100%的问题,因为它迫使你理解每件事情是如何相互作用的。


在Boost中,还有指针容器库。如果您只在对象容器的上下文中使用对象,那么与智能指针的标准容器相比,这些容器更高效、更易于使用。

在Windows上,有COM指针(iunknown、idispatch和friends),以及用于处理它们的各种智能指针(例如,ATL的ccompter和由Visual Studio中的"import"语句基于_com_ptr类自动生成的智能指针)。


std::tr1::shared_ptr通常是你最好的选择。


  • 共享所有权
  • 共享资源

当资源在多个对象之间共享时。BoostShared_ptr使用引用计数确保在找到每个人时取消分配资源。


另一种常用的形式是单一可转让所有人,由于避免了auto_ptr的分配语义的疯狂腐败所带来的问题,因此比auto_ptr更可取。

我只说以东十一〔二〕号。任何具有适当swap功能的类型都可以被认为是对某些内容的智能引用,在所有权转移到同一类型的另一个实例之前,它拥有这些内容,通过交换它们。每个实例保留其标识,但绑定到新内容。这就像一个安全的可重新保存的参考。

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

这意味着自动测试变得不那么必要——它只需要填补类型没有良好的swap功能的空白。但所有的性病容器都有。


yasper::ptr是一个轻量级的、Boost::共享的类似于ptr的选项。它在我(目前)的小项目中很有效。

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

Why write another C++ smart pointer?
There already exist several high
quality smart pointer implementations
for C++, most prominently the Boost
pointer pantheon and Loki's SmartPtr.
For a good comparison of smart pointer
implementations and when their use is
appropriate please read Herb Sutter's
The New C++: Smart(er) Pointers. In
contrast with the expansive features
of other libraries, Yasper is a
narrowly focused reference counting
pointer. It corresponds closely with
Boost's shared_ptr and Loki's
RefCounted/AllowConversion policies.
Yasper allows C++ programmers to
forget about memory management without
introducing the Boost's large
dependencies or having to learn about
Loki's complicated policy templates.
Philosophy

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

The last point can be dangerous, since
yasper permits risky (yet useful)
actions (such as assignment to raw
pointers and manual release)
disallowed by other implementations.
Be careful, only use those features if
you know what you're doing!


我不认为我能在我的设计中分享所有权。事实上,从我的头顶上我能想到的唯一有效的情况是飞锤模式。


  • 一业主
  • 升压::

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

我发现这是有用的,因为它可以很容易地重新存储,并释放,而不必担心泄漏。


  • 一个所有者:即在副本上删除
  • STD:AutoTypTR

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