简体   繁体   English

shared_ptr和weak_ptr的区别

[英]shared_ptr and weak_ptr differences

I am reading Scott Meyers "Effective C++" book. 我正在阅读Scott Meyers的“有效C ++”书。 It was mentioned that there are tr1::shared_ptr and tr1::weak_ptr act like built-in pointers, but they keep track of how many tr1::shared_ptrs point to an object. 有人提到过, 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_ptrs such 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_ptrs come in. 那就是tr1::weak_ptrs进入的地方。

My question is how cyclic data structures make the reference count above zero. 我的问题是循环数据结构如何使引用计数大于零。 I kindly request an example C++ program. 我请一个示例C ++程序。 How is the problem solved by weak_ptrs ? weak_ptrs如何解决问题? (again, with example please). (再次,请举个例子)。

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_ptrs again 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 ) cannot be used to solve the cycle problem - the programmer is solving the cycle problem. 因此, weak_ptr (基本上只是shared_ptr的精简版本) 不能用于解决循环问题-程序员正在解决循环问题。

To solve it, the programmer needs to be aware of the ownership relationship 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_ptr be 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_ptr cannot help. 答案是:如果对象之间的所有权不清楚,那么weak_ptr 无济于事。 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). 一种替代方法是使用具有完整垃圾收集的编程语言(例如:Java,C#,Go,Haskell),或使用与C / C ++一起使用的保守的(=不完美)垃圾收集器(例如:Boehm GC) 。

A shared_ptr wraps a reference counting mechanism around a raw pointer. shared_ptr将引用计数机制包装在原始指针周围。 So for each instance of the shared_ptr the reference count is increased by one. 因此,对于shared_ptr的每个实例,引用计数都增加一。 If two share_ptr objects refer the eachother they will never get deleted because they will never end up with a reference count of zero. 如果两个share_ptr对象share_ptr引用,它们将永远不会被删除,因为它们永远不会以零的引用计数结束。

weak_ptr points to a shared_ptr but does not increase its reference count.This means that the underying object can still be deleted even though there is a weak_ptr reference to it. weak_ptr指向shared_ptr ,但并不增加其引用count.This意味着,即使存在的underying对象仍然可以被删除weak_ptr引用。

The way that this works is that the weak_ptr can be use to create a shared_ptr for whenever one wants to use the underlying object. 这种工作方式是,只要有人想使用基础对象,就可以使用weak_ptr创建一个shared_ptr If however the object has already been deleted then an empty instance of a shared_ptr is returned. 但是,如果对象已被删除,则将返回shared_ptr的空实例。 Since the reference count on the underlying object is not increased with a weak_ptr reference, a circular reference will not result in the underlying object not being deleted. 由于底层对象的引用计数不会因使用weak_ptr引用而增加,因此循环引用不会导致底层对象未被删除。

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;  

Weak pointers just "observe" the managed object; 弱指针只是“观察”被管理对象。 they don't "keep it alive" or affect its lifetime. 他们不会“保持生命”或影响生命周期。 Unlike shared_ptr , when the last weak_ptr goes out of scope or disappears, the pointed-to object can still exist because the weak_ptr does not affect the lifetime of the object - it has no ownership rights. shared_ptr不同,当最后一个weak_ptr超出范围或消失时,指向对象仍然可以存在,因为weak_ptr不会影响对象的生存期-它没有所有权。 The weak_ptr can be used to determine whether the object exists, and to provide a shared_ptr that can be used to refer to it. weak_ptr可用于确定对象是否存在,并提供可用于引用该对象的shared_ptr

The definition of weak_ptr is designed to make it relatively foolproof, so as a result there is very little you can do directly with a weak_ptr . 设计weak_ptr的定义是为了使其相对简单,因此,几乎没有什么可以直接使用weak_ptr For example, you can't dereference it; 例如,您不能取消引用它; neither operator* nor operator-> is defined for a weak_ptr . 没有为weak_ptr定义operator*operator-> You can't access the pointer to the object with it - there is no get() function. 您无法使用它访问指向对象的指针-没有get()函数。 There is a comparison function defined so that you can store weak_ptrs in an ordered container, but that's all. 定义了一个比较函数,以便您可以将weak_ptrs存储在有序容器中,仅此而已。

All the above answer are WRONG. 以上所有答案都是错误的。 weak_ptr is 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_ptr if you have no resource other than memory to manage. 基本上,如果所有shared_ptr(s)是通过make_shared()allocate_shared()调用创建的,那么如果除了内存之外没有其他资源可以管理,则永远不需要weak_ptr These functions create the shared_ptr reference counter object with the object itself, and the memory will be freed at the same time. 这些函数使用对象本身创建shared_ptr参考计数器对象,并且将同时释放内存。

The only difference between weak_ptr and shared_ptr is that the weak_ptr allows the reference counter object to be kept after the actual object was freed. weak_ptrshared_ptr之间的唯一区别是, weak_ptr允许在释放实际对象之后保留参考计数器对象。 As a result, if you keep a lot of shared_ptr in a std::set the actual objects will occupy a lot of memory if they are big enough. 结果,如果您在std::set保留了许多shared_ptr ,则如果它们足够大,则实际对象将占用大量内存。 This problem can be solved by using weak_ptr instead. 此问题可以通过使用weak_ptr来解决。 In this case, you have to ensure the weak_ptr stored in the container is not expired before using it. 在这种情况下,必须确保容器中存储的weak_ptr在使用前没有过期。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM