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

动态分配的内存块的所有者有责任释放该内存。所以问题实际上变成了谁拥有内存。

在 C++ 中,所有权是通过 RAW 指针包装在其中的类型来记录的,因此在一个好的 (IMO) C++ 程序中,很少会看到 RAW 指针被传递(因为 RAW 指针没有推断出的所有权,因此我们不能告诉谁拥有内存,因此如果不仔细阅读文档,您就无法告诉谁负责所有权)。

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

所以问题是:

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

让我们为每个答案保留一种类型的语义所有权,以便可以单独对它们进行投票赞成和反对

概括:

从概念上讲,智能指针很简单,并且简单的实现也很容易。我见过许多尝试的实现,但它们总是以某种方式被破坏,而这些方式对于临时使用和示例来说并不明显。因此,我建议始终使用库中经过良好测试的“智能指针”,而不是自己推出。std::auto_ptr 或 boost 智能指针之一似乎满足了我的所有需求。

std::auto_ptr<T>:

单个人拥有该对象。
但允许转让所有权。

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

boost::scoped_ptr<T>

单个人拥有该对象。
不允许转让所有权。

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

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

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

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

升压::weak_ptr<T>

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

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

有帮助吗?

解决方案

对我来说,这3种已经满足了我的大部分需求:

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

weak_ptr - 与上面相同,但它是一个“奴隶” shared_ptr, 无法解除分配

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

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

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

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

其他提示

简单的 C++ 模型

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

该模型假设用户仅是他/她明确分配的内容的所有者. 。其他所有内容都会自动处置(在范围退出时或通过 RAII)。这是一个类似 C 的模型,通过以下事实进行扩展:大多数指针由对象拥有,这些对象将自动或在需要时(主要是在所述对象销毁时)释放它们,并且对象的生命周期是可预测的(RAII 是你的朋友,再次)。

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

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

智能指向 C++ 模型

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

  • 升压::shared_ptr
  • 提升::weak_ptr

结论

无论我描述什么模型, 除非有异常,否则接收指针是 不是 获得其所有权知道谁拥有谁仍然非常重要. 。即使对于大量使用引用和/或智能指针的 C++ 代码也是如此。

没有共同所有权。如果这样做,请确保仅使用您无法控制的代码。

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

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

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

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

从 boost 来看,还有 指针容器 图书馆。如果您仅在其容器的上下文中使用对象,那么它们比标准的智能指针容器更高效且更易于使用。

在 Windows 上,有 COM 指针(IUnknown、IDispatch 等)以及用于处理它们的各种智能指针(例如ATL 的 CComPtr 以及 Visual Studio 中的“import”语句根据以下内容自动生成的智能指针 _com_ptr 班级)。

  • 一位业主
  • 升压::scoped_ptr

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

我发现这很有用,因为它可以轻松地重新安装和释放,而不必担心泄漏

我认为我从来没有能够分享我的设计的所有权。事实上,从我的头脑中,我能想到的唯一有效的案例是享元模式。

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

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

为什么要再写一个 C++ 智能指针呢?C ++已经存在了几种高质量的智能指针实现,最突出的是Boost Pointer Pantheon和Loki的SmartPtr。要对智能指针实现进行良好的比较,并且当它们使用时,请阅读Herb Sutter的新C ++:智能(呃)指针。与其他库的宽敞特征相反,Yasper是一个狭窄的参考计数指针。它与Boost的共享_ptr和Loki的重新计算/允许转换策略密切相关。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允许其他实施不允许使用风险(但有用的)动作(例如对原始指针和手动释放的分配)。请小心,只有知道自己在做什么,仅使用这些功能!

还有另一种常用的单一可转让所有者形式,最好是 auto_ptr 因为它避免了由以下原因引起的问题 auto_ptr赋值语义的疯狂损坏。

我说的无非是 swap. 。具有合适的任何类型 swap 函数可以被理解为 智能参考 到某些内容,它拥有这些内容,直到所有权通过交换它们转移到相同类型的另一个实例。每个实例保留其身份,但绑定到新内容。它就像一个安全的可重新绑定的参考。

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

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

  • 业主一名:又名复制删除
  • std::auto_ptr

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top