c++ - 智能指针面试 - 智能指针的好处




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

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

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

在C ++中,所有权由一个RAW指针包装在内部的类型记录,因此在一个好的(IMO)C ++程序中,很少见到RAW RAW指针传递过来(因为RAW指针没有推断的所有权,所以我们不能告诉谁拥有记忆,因此如果没有仔细阅读文件,你不能分辨谁对所有权负责)。

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

所以问题是:

  • 人们会遇到什么类型的所有权语义?
  • 用什么标准类来实现这些语义?
  • 你觉得他们有什么用处?

让我们为每个答案保留一种语义所有权,以便他们可以单独进行投票

概要:

概念智能指针很简单,而且天真的实现很容易。 我见过很多尝试的实现,但总是以某种方式破坏了它,这对于偶然使用和示例并不明显。 因此,我建议您始终使用库中经过良好测试的“智能指针”,而不是滚动自己的。 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时,用于停止保留对象的循环。


简单的C ++模型

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

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

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

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

智能指向C ++模型

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

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

结论

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


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

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

为什么要写另一个C ++智能指针? 现在已经有几个高质量的智能指针实现用于C ++,最显着的是Boost指针神殿和Loki的SmartPtr。 为了更好地比较智能指针的实现以及何时使用它们,请阅读Herb Sutter的The New C ++:Smart(er)Pointers。 与其他库的扩展功能相比,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允许其他实现不允许的冒险(但有用的)操作(例如分配原始指针和手动释放)。 要小心,只有在知道自己在做什么的情况下才能使用这些功能!


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

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


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

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

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

auto_ptr - 当创建和取消分配发生在同一个函数内部时,或者当对象必须被视为一个所有者时。 当您将一个指针指定给另一个指针时,第二个从第一个'抢夺'对象。

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

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

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


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

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


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

我谈到除了swap 。 任何具有合适swap功能的类型都可以被认为是对某些内容的智能引用 ,它通过交换所有权直到所有权转移到另一个相同类型的实例。 每个实例都保留其身份,但会绑定到新内容。 这就像一个安全可重新引用的参考。

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

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


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

当对象的创建者想要明确地把所有权交给别人时。 这也是一种记录我向您提供的代码的方式,我不再跟踪它,因此请确保在完成后删除它。


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

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





ownership-semantics