智能指针:或谁拥有你的宝贝?

C ++全是关于内存所有权的
又名“ 所有权语义

一块dynamic分配的内存的所有者负责释放该内存。 所以这个问题真的成了谁拥有的记忆。

在C ++中,所有权由一个RAW指针所包含的内部typeslogging,因此在一个好的(IMO)C ++程序中,很less见到从RAW指针传来的RAW指针(因为RAW指针没有推断的所有权,所以我们不能告诉谁拥有记忆,因此没有仔细阅读文件,你不能分辨谁是负责所有权)。

反过来,很less见到存储在一个类中的RAW指针,每个RAW指针都存储在它自己的SMART指针包装器中。 ( 注意:如果你不拥有一个对象,你不应该存储它,因为你不知道什么时候它会超出范围并被销毁。)

所以问题是:

  • 人们遇到什么types的所有权语义?
  • 用什么标准的类来实现这些语义?
  • 什么情况下你觉得他们有用吗?

让我们为每个答案保留一种语义所有权,这样他们可以分别进行投票

概要:

从概念上讲,智能指针是简单的,一个简单的实现很容易。 我曾经见过很多尝试的实现,但是它们总是以某种方式被打破,这对于偶然使用和例子来说并不明显。 因此,我build议从图书馆总是使用经过良好testing的“智能指针”,而不是自己动手。 std :: auto_ptr或其中一个boost智能指针似乎涵盖了我所有的需求。

的std :: auto_ptr的<T>:

一个人拥有这个物体。
但是允许所有权转让。

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

升压:: scoped_ptr的<T>

一个人拥有这个物体。
不允许转让所有权。

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

boost :: shared_ptr <T>(std :: tr1 :: shared_ptr <T>)

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

用法:
======
当对象可以有多个在生命周期中不能在编译时确定的owers。

升压::的weak_ptr <T>

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

用法:
======
当只有循环维持一个共享的refcount时,用于停止保留对象的循环。

对我来说,这三种types涵盖了我的大部分需求:

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

weak_ptr – 与上面相同,但是它是shared_ptr的“奴隶”,不能释放

auto_ptr – 当创build和释放发生在同一个函数内部,或者当对象必须被视为一个所有者时。 当你将一个指针指向另一个指针时,第二个从第一个“偷取”对象。

我有我自己的这些实现,但它们也可以在Boost

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

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

简单的C ++模型

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

这个模型假设用户只是他/她明确分配的东西 。 其他一切都会自动处理(在范围退出,或通过RAII)。 这是一个类似C的模型,事实上大多数指针都是被对象拥有的,这些对象会自动或者在需要的时候释放它们(主要是在所谓的对象被破坏的情况下),并且对象的生存期是可预测的(RAII是你的朋友,再次)。

在这个模型中,原始的指针是自由stream通的,而且大多不是危险的(但是如果开发者足够聪明,只要可能,他/她就会使用引用)。

  • 生指针
  • 性病:: auto_ptr的
  • 提高:: scoped_ptr的

智能指向C ++模型

在充满智能指针的代码中,用户可以希望忽略对象的生命周期。 所有者永远不会是用户代码:它是智能指针本身(RAII,再次)。 问题是循环引用与引用计数的智能指针混合可能是致命的 ,所以你必须同时处理共享指针和弱指针。 所以你仍然有所有权考虑(弱指针可能指向没有,即使它比原始指针的优势是它可以告诉你这样)。

  • 提高:: shared_ptr的
  • 提高:: weak_ptr的

结论

无论我描述的模型如何, 除非是例外,接收指针都没有收到它的所有权知道谁拥有谁是非常重要的 。 即使对于大量使用引用和/或智能指针的C ++代码也是如此。

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

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

  • 共享所有权
  • 提高:: shared_ptr的

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

std::tr1::shared_ptr<Blah>通常是你最好的select。

从boost,还有指针容器库。 如果只能在容器的上下文中使用对象,则它们比标准的智能指针容器更高效,更易于使用。

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

  • 一个所有者
  • 提高:: scoped_ptr的

当你需要dynamic地分配内存,但是要确保它在块的每个出口点都被释放。

我发现这个有用的,因为它可以很容易地重新安置,释放而不必担心泄漏

我不认为我有能力在我的devise中分享所有权。 事实上,从我的头顶,我能想到的唯一有效的情况就是享元模式。

yasper :: ptr是一个轻量级的boost :: shared_ptr类似的替代方法。 它在我的(现在)小项目中运行良好。

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

为什么要写另一个C ++智能指针? 现在已经有几个高质量的C ++智能指针实现,最显着的是Boost指针神经元和Loki的SmartPtr。 对于智能指针实现的一个很好的比较,当它们的使用是合适的,请阅读Herb Sutter的新C ++:智能(指针)指针。 与其他库的扩展function相比,Yasper是一个狭义的引用计数指针。 它与Boost的shared_ptr和Loki的RefCounted / AllowConversion策略密切对应。 Yasper允许C ++程序员在不引入Boost的大型依赖或不得不学习Loki复杂的策略模板的情况下,忘记内存pipe理。 哲学

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

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

还有另外一种经常使用的single-transferable-owner的forms,最好是auto_ptr因为它避免了auto_ptr疯狂的赋值语义腐败所导致的问题。

我说的是swap 。 任何具有合适swapfunction的types都可以被认为是对某些内容的智能引用 ,这些内容在所有权转移到另一个相同types的实例之前,通过交换它们而拥有。 每个实例保留其身份,但绑定到新的内容。 这就像一个安全的可重新引用的参考。

(这是一个聪明的参考,而不是一个聪明的指针,因为你不必明确地解引用它来获取内容。)

这意味着auto_ptr变得不那么必要了 – 只需要填补types没有很好swapfunction的空白。 但是所有的std容器都行。

  • 一个所有者:又名副本删除
  • 性病:: auto_ptr的

当对象的创build者想明确地把所有权交给别人时。 这也是logging在我给你这个代码的一种方式,我不再追踪它,所以确保你完成后删除它。