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

发布于 2024-07-05 08:52:57 字数 1297 浏览 9 评论 0 原文

C++ 的核心是内存所有权 - 又名所有权语义

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

在 C++ 中,所有权是通过包装在其中的原始指针类型来记录的,因此在一个好的(IMO)C++程序中,它是非常罕见的(罕见,而不是从不< /em>)来查看传递的原始指针(因为原始指针没有推断的所有权,因此我们无法判断谁拥有内存,因此如果不仔细阅读文档,您就无法判断谁负责所有权)。

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

所以问题是:

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

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

简介:

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

std::auto_ptr

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

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

boost::scoped_ptr

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

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

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

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

用法:当一个对象可以具有多个生命周期且无法在编译时确定的生命周期时。

boost::weak_ptr

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

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

C++ is all about memory ownership - aka ownership semantics.

It is the responsibility of the owner of a chunk of dynamically allocated memory to release that memory. So the question really becomes who owns the memory.

In C++ ownership is documented by the type a raw pointer is wrapped inside thus in a good (IMO) C++ program it is very rare (rare, not never) to see raw pointers passed around (as raw pointers have no inferred ownership thus we can not tell who owns the memory and thus without careful reading of the documentation you can't tell who is responsible for ownership).

Conversely, it is rare to see raw pointers stored in a class each raw pointer is stored within its own smart pointer wrapper. (N.B.: If you don't own an object you should not be storing it because you can not know when it will go out of scope and be destroyed.)

So the question:

  • What type of ownership semantic have people come across?
  • What standard classes are used to implement those semantics?
  • In what situations do you find them useful?

Lets keep 1 type of semantic ownership per answer so they can be voted up and down individually.

Summary:

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>:

Single person owns the object. Transfer of ownership is allowed.

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

boost::scoped_ptr<T>

Single person owns the object. Transfer of ownership is NOT allowed.

Usage: Used to show explicit ownership. Object will be destroyed by destructor or when explicitly reset.

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

Multiple ownership. This is a simple reference counted pointer. When the reference count reaches zero, the object is destroyed.

Usage: When an object can have multiple owers with a lifetime that can not be determined at compile time.

boost::weak_ptr<T>:

Used with shared_ptr<T> in situations where a cycle of pointers may happen.

Usage: Used to stop cycles from retaining objects when only the cycle is maintaining a shared refcount.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

二智少女 2024-07-12 08:52:57

简单的 C++ 模型

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

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

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

  • 原始指针
  • std::auto_ptr
  • boost::scoped_ptr

智能指向 C++ 模型

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

  • boost::shared_ptr
  • boost::weak_ptr

结论

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

Simple C++ Model

In most modules I saw, by default, it was assumed that receiving pointers was not receiving ownership. In fact, functions/methods abandoning ownership of a pointer were both very rare and explicitly expressed that fact in their documentation.

This model assumes that the user is owner only of what he/she explicitly allocates. Everything else is automatically disposed of (at scope exit, or through RAII). This is a C-like model, extended by the fact most pointers are owned by objects that will deallocate them automatically or when needed (at said objects destruction, mostly), and that the life duration of objects are predictable (RAII is your friend, again).

In this model, raw pointers are freely circulating and mostly not dangerous (but if the developer is smart enough, he/she will use references instead whenever possible).

  • raw pointers
  • std::auto_ptr
  • boost::scoped_ptr

Smart Pointed C++ Model

In a code full of smart pointers, the user can hope to ignore the lifetime of objects. The owner is never the user code: It is the smart pointer itself (RAII, again). The problem is that circular references mixed with reference counted smart pointers can be deadly, so you have to deal both with both shared pointers and weak pointers. So you have still ownership to consider (the weak pointer could well point to nothing, even if its advantage over raw pointer is that it can tell you so).

  • boost::shared_ptr
  • boost::weak_ptr

Conclusion

No matter the models I describe, unless exception, receiving a pointer is not receiving its ownership and it is still very important to know who owns who. Even for C++ code heavily using references and/or smart pointers.

向地狱狂奔 2024-07-12 08:52:57

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

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

weak_ptr - 与上面相同,但它是“从属”对于 shared_ptr,无法释放

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

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

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

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

For me, these 3 kinds cover most of my needs:

shared_ptr - reference-counted, deallocation when the counter reaches zero

weak_ptr - same as above, but it's a 'slave' for a shared_ptr, can't deallocate

auto_ptr - when the creation and deallocation happen inside the same function, or when the object has to be considered one-owner-only ever. When you assign one pointer to another, the second 'steals' the object from the first.

I have my own implementation for these, but they are also available in Boost.

I still pass objects by reference (const whenever possible), in this case the called method must assume the object is alive only during the time of call.

There's another kind of pointer that I use that I call hub_ptr. It's when you have an object that must be accessible from objects nested in it (usually as a virtual base class). This could be solved by passing a weak_ptr to them, but it doesn't have a shared_ptr to itself. As it knows these objects wouldn't live longer than him, it passes a hub_ptr to them (it's just a template wrapper to a regular pointer).

2024-07-12 08:52:57

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

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

Don't have shared ownership. If you do, make sure it's only with code you don't control.

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

回忆那么伤 2024-07-12 08:52:57
  • 共享所有权
  • boost::shared_ptr

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

  • Shared Ownership
  • boost::shared_ptr

When a resource is shared between multiple objects.
The boost shared_ptr uses reference counting to make sure the resource is de-allocated when everybody is finsihed.

2024-07-12 08:52:57

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

std::tr1::shared_ptr<Blah> is quite often your best bet.

阿楠 2024-07-12 08:52:57

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

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

From boost, there's also the pointer container library. These are a bit more efficient and easier to use than a standard container of smart pointers, if you'll only be using the objects in the context of their container.

On Windows, there are the COM pointers (IUnknown, IDispatch, and friends), and various smart pointers for handling them (e.g. the ATL's CComPtr and the smart pointers auto-generated by the "import" statement in Visual Studio based on the _com_ptr class).

℡Ms空城旧梦 2024-07-12 08:52:57

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

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

为什么要再写一个 C++ 智能指针?
已经存在几个高
高质量智能指针实现
对于 C++,最突出的是 Boost
指针万神殿和洛基的 SmartPtr。
为了更好地比较智能指针
实现以及何时使用它们
适当的请阅读 Herb Sutter 的
新 C++:智能指针。 在
与广阔的特征形成鲜明对比
与其他库相比,Yasper 是
狭义的引用计数
指针。 它密切对应
Boost 的shared_ptr 和Loki 的
RefCounted/AllowConversion 策略。
Yasper 允许 C++ 程序员
忘记内存管理而不
介绍Boost的大
依赖或必须了解
Loki 复杂的策略模板。
理念

* 小(包含在单个标头中) 
  * 简单(代码中没有什么花哨的东西,易于理解) 
  * 最大兼容性(替代哑指针) 
  

最后一点可能很危险,因为
yasper 允许有风险(但有用)
操作(例如分配给原始
指针和手动释放)
其他实现不允许。
请小心,仅在以下情况下使用这些功能
你知道你在做什么!

yasper::ptr is a lightweight, boost::shared_ptr like alternative. It works well in my (for now) small project.

In the web page at http://yasper.sourceforge.net/ it's described as follows:

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

* 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!

家住魔仙堡 2024-07-12 08:52:57

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

I don't think I ever was in a position to have shared ownership in my design. In fact, from the top of my head the only valid case I can think of is Flyweight pattern.

兰花执着 2024-07-12 08:52:57
  • 一个所有者
  • boost::scoped_ptr

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

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

  • One Owner
  • boost::scoped_ptr

When you need to allocate memory dynamically but want to be sure it gets deallocated on every exit point of the block.

I find this usefull as it can easily be reseated, and released without ever having to worry about a leak

老街孤人 2024-07-12 08:52:57
  • 一个所有者:又称删除复制
  • std::auto_ptr

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

  • One Owner: Aka delete on Copy
  • std::auto_ptr

When the creator of the object wants to explicitly hand ownership to somebody else.
This is also a way documenting in the code I am giving this to you and I am no longer tracking it so make sure you delete it when you are finished.

何必那么矫情 2024-07-12 08:52:57

还有另一种经常使用的单一可转让所有者形式,它比 auto_ptr 更可取,因为它避免了 auto_ptr 赋值语义的疯狂损坏所引起的问题。

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

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

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

There is another frequently used form of single-transferable-owner, and it is preferable to auto_ptr because it avoids the problems caused by auto_ptr's insane corruption of assignment semantics.

I speak of none other than swap. Any type with a suitable swap function can be conceived of as a smart reference to some content, which it owns until such time as ownership is transferred to another instance of the same type, by swapping them. Each instance retains its identity but gets bound to new content. It's like a safely rebindable 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.)

This means that auto_ptr becomes less necessary - it's only needed to fill the gaps where types don't have a good swap function. But all std containers do.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文