智能指针: 谁拥有该对象?

C + + 完全是关于内存所有权的——也就是 所有权语义

动态分配的内存块的所有者有责任释放该内存。所以问题变成了谁拥有这段记忆。

在 C + + 的所有权类型中,生的指针被封装在内部,因此在一个好的(IMO) C + + 程序中,很少看到原始指针传递(rare,而不是 永远不会)(因为原始指针没有推断出的所有权,所以我们不能判断谁拥有内存,因此如果不仔细阅读文档,你就不能判断谁对所有权负责)。

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

So the question:

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

让每个答案保持一种语义所有权类型,这样它们就可以单独上下投票。

摘要:

Conceptually, smart pointers are simple and a naive implementation is easy. I have seen many attempted implementations, but invariably they are broken in some way that is not obvious to casual use and examples. Thus I recommend always using well tested smart pointers from a library rather than rolling your own. std::auto_ptr or one of the Boost smart pointers seem to cover all my needs.

std::auto_ptr<T>:

物品归单个人所有,允许转让所有权。

Usage: This allows you to define interfaces that show the explicit transfer of ownership.

boost::scoped_ptr<T>

物品属于个人所有,不允许转让所有权。

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

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

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

用法: 当一个对象可以有多个生存期不能在编译时确定的所有者时。

返回文章页面

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

用法: 当只有循环在维护共享重新计数时,用于阻止循环保留对象。

61040 次浏览
  • 一个所有者: 也就是删除拷贝
  • Auto _ ptr

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

  • Shared Ownership
  • : share _ ptr

当一个资源在多个对象之间共享时。 The boost shared_ptr uses reference counting to make sure the resource is de-allocated when everybody is finsihed.

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

  • 一个主人
  • Boost: : scoped _ ptr

当您需要动态分配内存,但是希望确保在块的每个出口点上释放内存时。

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

对我来说,以下三种方式可以满足我的大部分需求:

引用计数,当计数器达到零时释放

weak_ptr-和上面一样,但是它是 shared_ptr的“奴隶”,不能释放

auto_ptr-当创建和释放发生在同一个函数中,或者当对象必须被认为是单所有者的时候。当你分配一个指针给另一个时,第二个“窃取”对象从第一个。

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

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

我使用的另一种指针称为 < em > hub _ ptr 。当您有一个对象必须可以从嵌套在其中的对象访问时(通常作为一个虚拟基类)。这个问题可以通过向它们传递一个 weak_ptr来解决,但是它本身并没有一个 shared_ptr。因为它知道这些对象的存活时间不会比他长,所以它向它们传递一个 hub _ ptr (它只是一个指向常规指针的模板包装器)。

我不认为我曾经在我的设计有共同所有权的位置。事实上,我能想到的唯一有效的案例就是享元模式。

不要共享所有权。如果你这样做了,请确保只使用你不能控制的代码。

That solves 100% of the problems, since it forces you to understand how everything interacts.

简单的 C + + 模型

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

这个模型假设用户只是他/她显式分配的 的所有者。其他所有东西都会自动处理掉(在范围出口或通过 RAII)。这是一个类似 C 的模型,扩展了这样一个事实: 大多数指针由对象所拥有,这些对象会自动或在需要时释放它们(大多数情况下,在上述对象破坏时) ,而且对象的生命周期是可预测的(RAII 是你的朋友,再次)。

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

  • 原始指针
  • Auto _ ptr
  • Boost: : scoped _ ptr

智能点 C + + 模型

在充满智能指针的代码中,用户可能希望忽略对象的生存期。所有者从来不是用户代码: 它是智能指针本身(同样是 RAII)。因此,您必须同时处理共享指针和弱指针。因此您仍然需要考虑所有权问题(弱指针很可能什么也不指向,即使它比原始指针的优势在于它可以告诉您这一点)。

  • : share _ ptr
  • boost::weak_ptr

Conclusion

无论我所描述的模型是什么,除非例外,接收指针是 < em > not 接收其所有权it is still very important to know who owns who都是如此。即使是 C + + 代码也大量使用引用和/或智能指针。

Ptr 是一个轻量级的、类似于 ost: : share _ ptr 的替代方案,它在我的(目前)小项目中运行良好。

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

为什么要编写另一个 C + + 智能指针? 已经有几个高 高质量智能指针实现 for C++, most prominently the Boost 指针万神殿和洛基的 SmartPtr。 为了更好地比较智能指针 实现,以及何时使用它们 请阅读 Herb Sutter 的 The New C++: Smart(er) Pointers. In 与膨胀的特征形成对比 在其他图书馆中,Yasper 是一个 狭义聚焦参考计数 指针。它与 Boost's shared_ptr and Loki's 重新计算/允许转换政策。 Yasper 允许 C + + 程序员 忘记内存管理 介绍一下 Boost 的大号 依赖性或者必须了解 洛基复杂的策略模板。 哲学

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

最后一点可能很危险,因为 Yasper 允许有风险(但有用) 动作(例如对原始 指针和手动释放) 被其他实现禁止。 注意,只有在下列情况下才使用这些特性 你知道你在做什么!

另外还有升级版的 指针容器指针容器库。与标准的智能指针容器相比,如果您只在其容器的上下文中使用对象,那么它们会更有效、更容易使用。

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

还有另一种常用的单一可转让所有者形式,它比 auto_ptr更可取,因为它避免了由于 auto_ptr疯狂地破坏分配语义而引起的问题。

我说的不是别人,正是 swap。任何具有适当 swap功能的类型都可以被看作某些内容的 smart reference,它拥有这些内容,直到通过交换它们,所有权被转移到同一类型的另一个实例。每个实例保留其标识,但绑定到新内容。这就像一个安全的可复制的参考。

(It's a smart reference rather than a smart pointer because you don't have to explicitly dereference it to get at the content.)

这意味着 auto _ ptr 变得不那么必要了——它只需要填补类型没有良好 swap函数的空白。但所有性病容器都有。