C++ 指针、智能指针还是共享指针?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/417481/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:14:36  来源:igfitidea点击:

Pointers, smart pointers or shared pointers?

c++pointers

提问by tunnuz

I am programming with normal pointers, but I have heard about libraries like Boost that implement smart pointers. I have also seen that in Ogre3D rendering engine there is a deep use of shared pointers.

我正在使用普通指针编程,但我听说过像 Boost 这样实现智能指针的库。我还看到在 Ogre3D 渲染引擎中深入使用了共享指针。

What exactly is the difference between the three, and should I stick on using just a type of them?

三者之间究竟有什么区别,我应该坚持只使用其中的一种吗?

回答by hazzen

Sydius outlined the types fairly well:

Sydius 很好地概述了这些类型:

  • Normal pointersare just that - they point to some thing in memory somewhere. Who owns it? Only the comments will let you know. Who frees it? Hopefully the owner at some point.
  • Smart pointersare a blanket term that cover many types; I'll assume you meant scoped pointer which uses the RAIIpattern. It is a stack-allocated object that wraps a pointer; when it goes out of scope, it calls delete on the pointer it wraps. It "owns" the contained pointer in that it is in charge of deleteing it at some point. They allow you to get a raw reference to the pointer they wrap for passing to other methods, as well as releasingthe pointer, allowing someone else to own it. Copying them does not make sense.
  • Shared pointersis a stack-allocated object that wraps a pointer so that you don't have to know who owns it. When the last shared pointer for an object in memory is destructed, the wrapped pointer will also be deleted.
  • 普通指针就是这样——它们指向内存中某处的某些东西。谁拥有它?只有评论会让你知道。谁释放它?希望楼主在某个时候。
  • 智能指针是一个涵盖多种类型的总称。我假设您指的是使用RAII模式的作用域指针。它是一个封装了指针的堆栈分配对象;当它超出范围时,它会在它包装的指针上调用 delete。它“拥有”包含的指针,因为它负责在某个时候删除它。它们允许您获得对它们包装的指针的原始引用以传递给其他方法,以及释放指针,允许其他人拥有它。复制它们没有意义。
  • 共享指针是一个堆栈分配的对象,它包装了一个指针,这样您就不必知道谁拥有它。当内存中对象的最后一个共享指针被销毁时,包装的指针也将被删除。

How about when you should use them? You will either make heavy use of scoped pointers or shared pointers. How many threads are running in your application? If the answer is "potentially a lot", shared pointers can turn out to be a performance bottleneck if used everywhere. The reason being that creating/copying/destructing a shared pointer needs to be an atomic operation, and this can hinder performance if you have many threads running. However, it won't always be the case - only testing will tell you for sure.

什么时候应该使用它们呢?您将大量使用作用域指针或共享指针。您的应用程序中有多少线程正在运行?如果答案是“可能很多”,那么如果到处使用共享指针,就会成为性能瓶颈。原因是创建/复制/销毁共享指针需要是原子操作,如果您有许多线程正在运行,这可能会影响性能。但是,情况并非总是如此 - 只有测试才能确定地告诉您。

There is an argument (that I like) against shared pointers - by using them, you are allowing programmers to ignore who owns a pointer. This can lead to tricky situations with circular references (Java will detect these, but shared pointers cannot) or general programmer laziness in a large code base.

有一个反对共享指针的论点(我喜欢) - 通过使用它们,您允许程序员忽略谁拥有指针。这可能导致循环引用的棘手情况(Java 会检测到这些,但共享指针不能)或大型代码库中的一般程序员懒惰。

There are two reasons to use scoped pointers. The first is for simple exception safety and cleanup operations - if you want to guarantee that an object is cleaned up no matter what in the face of exceptions, and you don't want to stack allocate that object, put it in a scoped pointer. If the operation is a success, you can feel free to transfer it over to a shared pointer, but in the meantime save the overhead with a scoped pointer.

使用作用域指针有两个原因。第一个是用于简单的异常安全和清理操作——如果你想保证一个对象无论遇到什么异常都会被清理,并且你不想堆栈分配那个对象,把它放在一个作用域指针中。如果操作成功,您可以随意将其转移到共享指针,但同时使用作用域指针节省开销。

The other case is when you want clear object ownership. Some teams prefer this, some do not. For instance, a data structure may return pointers to internal objects. Under a scoped pointer, it would return a raw pointer or reference that should be treated as a weak reference - it is an error to access that pointer after the data structure that owns it is destructed, and it is an error to delete it. Under a shared pointer, the owning object can't destruct the internal data it returned if someone still holds a handle on it - this could leave resources open for much longer than necessary, or much worse depending on the code.

另一种情况是当您想要明确的对象所有权时。有些团队喜欢这个,有些则不喜欢。例如,数据结构可能返回指向内部对象的指针。在作用域指针下,它将返回一个应被视为弱引用的原始指针或引用 - 在拥有它的数据结构被破坏后访问该指针是错误的,而删除它是错误的。在共享指针下,如果有人仍然持有它的句柄,则拥有对象不能破坏它返回的内部数据——这可能会使资源打开的时间比必要的长得多,或者更糟,具体取决于代码。

回答by tunnuz

the term "smart pointer" includesshared pointers, auto pointers, locking pointers and others. you meant to say auto pointer (more ambiguously known as "owning pointer"), not smart pointer.

术语“智能指针”包括共享指针、自动指针、锁定指针等。你的意思是说自动指针(更模糊地称为“拥有指针”),而不是智能指针。

Dumb pointers (T*) are never the best solution. They make you do explicit memory management, which is verbose, error prone, and sometimes nigh impossible. But more importantly, they don't signal your intent.

哑指针 (T*) 从来都不是最好的解决方案。它们让你进行显式的内存管理,这是冗长的、容易出错的,有时几乎是不可能的。但更重要的是,它们不会表明您的意图。

Auto pointers delete the pointee at destruction. For arrays, prefer encapsulations like vector and deque. For other objects, there's very rarely a need to store them on the heap - just use locals and object composition. Still the need for auto pointers arises with functions that return heap pointers -- such as factories and polymorphic returns.

自动指针在销毁时删除指针。对于数组,更喜欢像 vector 和 deque 这样的封装。对于其他对象,很少需要将它们存储在堆上——只需使用局部变量和对象组合。返回堆指针的函数仍然需要自动指针——例如工厂和多态返回。

Shared pointers delete the pointee when the last shared pointer to it is destroyed. This is useful when you want a no-brainer, open-ended storage scheme where expected lifetime and ownership can vary widely depending on the situation. Due to the need to keep an (atomic) counter, they're a bit slower than auto pointers. Some say half in jest that shared pointers are for people who can't design systems -- judge for yourself.

当指向它的最后一个共享指针被销毁时,共享指针删除指针。当您想要一个简单的、开放式的存储方案时,这很有用,其中预期寿命和所有权可能因情况而异。由于需要保留一个(原子)计数器,它们比自动指针慢一点。有人半开玩笑地说,共享指针是为不会设计系统的人准备的——你自己判断。

For an essential counterpart to shared pointers, look up weak pointers too.

对于共享指针的基本对应物,也可以查找弱指针。

回答by Sydius

Smart pointers will clean themselves up after they go out of scope (thereby removing fear of most memory leaks). Shared pointers are smart pointers that keep a count of how many instances of the pointer exist, and only clean up the memory when the count reaches zero. In general, only use shared pointers (but be sure to use the correct kind--there is a different one for arrays). They have a lot to do with RAII.

智能指针在超出范围后会自行清理(从而消除对大多数内存泄漏的恐惧)。共享指针是智能指针,它记录指针存在的实例数量,并且仅在计数达到零时才清理内存。一般来说,只使用共享指针(但一定要使用正确的类型——数组有不同的类型)。它们与RAII有很大关系。

回答by d0k

To avoid memory leaks you may use smart pointers whenever you can. There are basically 2 different types of smart pointers in C++

为了避免内存泄漏,您可以尽可能使用智能指针。C++ 中基本上有两种不同类型的智能指针

  • Reference counted (e.g. boost::shared_ptr / std::tr1:shared_ptr)
  • non reference counted (e.g. boost::scoped_ptr / std::auto_ptr)
  • 引用计数(例如 boost::shared_ptr / std::tr1:shared_ptr)
  • 非引用计数(例如 boost::scoped_ptr / std::auto_ptr)

The main difference is that reference counted smart pointers can be copied (and used in std:: containers) while scoped_ptr cannot. Non reference counted pointers have almost no overhead or no overhead at all. Reference counting always introduces some kind of overhead.

主要区别在于引用计数智能指针可以复制(并在 std:: 容器中使用)而 scoped_ptr 不能。非引用计数指针几乎没有开销或根本没有开销。引用计数总是会引入某种开销。

(I suggest to avoid auto_ptr, it has some serious flaws if used incorrectly)

(我建议避免使用 auto_ptr,如果使用不当它有一些严重的缺陷)

回答by SmacL

To add a small bit to Sydius' answer, smart pointers will often provide a more stable solution by catching many easy to make errors. Raw pointers will have some perfromance advantages and can be more flexible in certain circumstances. You may also be forced to use raw pointers when linking into certain 3rd party libraries.

为 Sydius 的回答补充一点,智能指针通常会通过捕获许多容易犯的错误来提供更稳定的解决方案。原始指针将具有一些性能优势,并且在某些情况下可以更加灵活。在链接到某些 3rd 方库时,您也可能被迫使用原始指针。