C++ shared_ptr 和 weak_ptr 的区别

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4984381/
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-28 17:05:04  来源:igfitidea点击:

shared_ptr and weak_ptr differences

c++c++11shared-ptrweak-ptrcyclic-reference

提问by venkysmarty

I am reading Scott Meyers "Effective C++" book. It was mentioned that there are tr1::shared_ptrand tr1::weak_ptract like built-in pointers, but they keep track of how many tr1::shared_ptrspoint to an object.

我正在阅读 Scott Meyers 的“Effective C++”一书。有人提到,有tr1::shared_ptrtr1::weak_ptr像内建指针,但他们跟踪有多少tr1::shared_ptrs指向一个对象。

This is known as reference counting. This works well in preventing resource leaks in acyclic data structures, but if two or more objects contain tr1::shared_ptrssuch that a cycle is formed, the cycle may keep each other's reference count above zero, even when all external pointers to the cycle have been destroyed.

这称为引用计数。这在防止非循环数据结构中的资源泄漏方面很有效,但是如果两个或多个对象包含tr1::shared_ptrs这样一个循环,则循环可能会将彼此的引用计数保持在零以上,即使所有指向循环的外部指针都已被销毁。

That's where tr1::weak_ptrscome in.

那就是tr1::weak_ptrs进来的地方。

My question is how cyclic data structures make the reference count above zero. I kindly request an example C++ program. How is the problem solved by weak_ptrs? (again, with example please).

我的问题是循环数据结构如何使引用计数高于零。我恳请请求一个示例 C++ 程序。问题是如何解决的weak_ptrs?(再次,请举例)。

采纳答案by doron

A shared_ptrwraps a reference counting mechanism around a raw pointer. So for each instance of the shared_ptrthe reference count is increased by one. If two share_ptrobjects refer the eachother they will never get deleted because they will never end up with a reference count of zero.

Ashared_ptr在原始指针周围包装了一个引用计数机制。所以对于每个实例shared_ptr的引用计数加一。如果两个share_ptr对象相互引用,它们将永远不会被删除,因为它们的引用计数永远不会为零。

weak_ptrpoints to a shared_ptrbut does not increase its reference count.This means that the underying object can still be deleted even though there is a weak_ptrreference to it.

weak_ptr指向 ashared_ptr但不增加其引用计数。这意味着即使存在对它的引用,底层对象仍然可以被删除weak_ptr

The way that this works is that the weak_ptrcan be use to create a shared_ptrfor whenever one wants to use the underlying object. If however the object has already been deleted then an empty instance of a shared_ptris returned. Since the reference count on the underlying object is not increased with a weak_ptrreference, a circular reference will not result in the underlying object not being deleted.

这样做的方式是,只要想要使用底层对象,weak_ptr就可以使用它来创建shared_ptrfor。然而,如果该对象已被删除,shared_ptr则返回一个空的 a 实例。由于底层对象的引用计数不会随着weak_ptr引用的增加而增加,循环引用不会导致底层对象不被删除。

回答by

Let me repeat your question: "My question, how cyclic data structures makes reference count above zero, kindly request to show with example in C++ program. How the problem is solved by weak_ptrsagain with example please."

让我重复您的问题:“我的问题,循环数据结构如何使引用计数高于零,请在 C++ 程序中以示例显示。weak_ptrs请再次以示例解决问题。”

The problem occurs with C++ code like this (conceptually):

像这样的 C++ 代码会出现问题(概念上):

class A { shared_ptr<B> b; ... };
class B { shared_ptr<A> a; ... };
shared_ptr<A> x(new A);  // +1
x->b = new B;            // +1
x->b->a = x;             // +1
// Ref count of 'x' is 2.
// Ref count of 'x->b' is 1.
// When 'x' leaves the scope, there will be a memory leak:
// 2 is decremented to 1, and so both ref counts will be 1.
// (Memory is deallocated only when ref count drops to 0)

To answer the second part of your question: It is mathematically impossible for reference counting to deal with cycles. Therefore, a weak_ptr(which is basically just a stripped down version of shared_ptr) cannotbe used to solve the cycle problem - the programmer is solving the cycle problem.

回答问题的第二部分:引用计数在数学上不可能处理循环。因此, a weak_ptr(基本上只是 的精简版本shared_ptr不能用于解决循环问题——程序员正在解决循环问题。

To solve it, the programmer needs to be aware of the ownershiprelationship among the objects, or needs to invent an ownership relationship if no such ownership exists naturally.

为了解决这个问题,程序员必须知道的所有权的对象,或需求之间的关系,如果没有这种所有权自然的存在是为了创造一个所有权关系。

The above C++ code can be changed so that A owns B:

可以更改上面的 C++ 代码,使 A 拥有 B:

class A { shared_ptr<B> b; ... };
class B { weak_ptr<A>   a; ... };
shared_ptr<A> x(new A); // +1
x->b = new B;           // +1
x->b->a = x;            // No +1 here
// Ref count of 'x' is 1.
// Ref count of 'x->b' is 1.
// When 'x' leaves the scope, its ref count will drop to 0.
// While destroying it, ref count of 'x->b' will drop to 0.
// So both A and B will be deallocated.

A crucial question is: Can weak_ptrbe used in case the programmer cannot tell the ownership relationship and cannot establish any static ownership because of lack of privilege or lack of information?

一个关键的问题是:weak_ptr如果程序员因为没有特权或信息不足而无法说出所有权关系,无法建立任何静态所有权,可以使用吗?

The answer is: If ownership among objects is unclear, weak_ptrcannothelp. If there is a cycle, the programmer has to find it and break it. An alternative remedy is to use a programming language with full garbage collection (such as: Java, C#, Go, Haskell), or to use a conservative (=imperfect) garbage collector which works with C/C++ (such as: Boehm GC).

答案是:如果对象之间的所有权不清楚,weak_ptr无济于事。如果有一个循环,程序员必须找到它并打破它。另一种补救方法是使用具有完整垃圾收集功能的编程语言(例如:Java、C#、Go、Haskell),或者使用与 C/C++ 配合使用的保守(=不完美)垃圾收集器(例如:Boehm GC) .

回答by newprint

For future readers.
Just want to point out that explanation given by Atom is excellent, here is working code

对于未来的读者。
只是想指出 Atom 给出的解释非常好,这里是工作代码

#include <memory> // and others
using namespace std;

    class B; // forward declaration 
    // for clarity, add explicit destructor to see that they are not called
    class A { public: shared_ptr<B> b; ~A() {cout << "~A()" << endl; } };  
    class B { public: shared_ptr<A> a; ~B() {cout << "~B()" << endl; } };     
    shared_ptr<A> x(new A);  //x->b share_ptr is default initialized
    x->b = make_shared<B>(); // you can't do "= new B" on shared_ptr                      
    x->b->a = x;
    cout << x.use_count() << endl;  

回答by peterDriscoll

Weak pointers just "observe" the managed object; they don't "keep it alive" or affect its lifetime. Unlike shared_ptr, when the last weak_ptrgoes out of scope or disappears, the pointed-to object can still exist because the weak_ptrdoes not affect the lifetime of the object - it has no ownership rights. The weak_ptrcan be used to determine whether the object exists, and to provide a shared_ptrthat can be used to refer to it.

弱指针只是“观察”托管对象;它们不会“让它保持活力”或影响它的寿命。与 不同shared_ptr,当最后一个weak_ptr超出范围或消失时,指向的对象仍然可以存在,因为weak_ptr不影响对象的生命周期 - 它没有所有权。所述weak_ptr可用于确定对象是否存在,并提供一种shared_ptr可用于指它。

The definition of weak_ptris designed to make it relatively foolproof, so as a result there is very little you can do directly with a weak_ptr. For example, you can't dereference it; neither operator*nor operator->is defined for a weak_ptr. You can't access the pointer to the object with it - there is no get()function. There is a comparison function defined so that you can store weak_ptrsin an ordered container, but that's all.

的定义weak_ptr旨在使其相对万无一失,因此您几乎无法直接使用weak_ptr. 例如,您不能取消引用它;既没有operator*也没有operator->为 a 定义weak_ptr。您无法使用它访问指向对象的指针 - 没有get()函数。定义了一个比较函数,以便您可以将其存储weak_ptrs在有序容器中,仅此而已。

回答by Earth Engine

All the above answer are WRONG. weak_ptris NOT used to break cyclic references, they have another purpose.

以上所有答案都是错误的。weak_ptr不用于中断循环引用,它们还有另一个目的。

Basically, if all shared_ptr(s)were created by make_shared()or allocate_shared()calls, you will NEVER need weak_ptrif you have no resource other than memory to manage. These functions create the shared_ptrreference counter object with the object itself, and the memory will be freed at the same time.

基本上,如果所有内容shared_ptr(s)都是由make_shared()allocate_shared()调用创建的,那么weak_ptr如果除了内存之外没有其他资源需要管理,您将永远不需要。这些函数shared_ptr用对象本身创建引用计数器对象,同时释放内存。

The only difference between weak_ptrand shared_ptris that the weak_ptrallows the reference counter object to be kept after the actual object was freed. As a result, if you keep a lot of shared_ptrin a std::setthe actual objects will occupy a lot of memory if they are big enough. This problem can be solved by using weak_ptrinstead. In this case, you have to ensure the weak_ptrstored in the container is not expired before using it.

weak_ptr和之间的唯一区别shared_ptrweak_ptr允许在释放实际对象后保留引用计数器对象。因此,如果您shared_ptr在 a 中保留大量std::set对象,如果它们足够大,实际对象将占用大量内存。这个问题可以通过使用weak_ptr来解决。在这种情况下,您必须确保weak_ptr容器中存储的内容没有过期才能使用。