shared_ptr和weak_ptr差异

ven*_*rty 69 c++ shared-ptr weak-ptr cyclic-reference c++11

我正在阅读Scott Meyers的"Effective C++"一书.有人提到有内置指针tr1::shared_ptrtr1::weak_ptr行为,但它们跟踪tr1::shared_ptrs指向对象的数量.

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

那是tr1::weak_ptrs进来的地方.

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

小智 111

让我重复一下你的问题:"我的问题是,循环数据结构如何使引用计数高于零,请在C++程序中以示例的形式显示.如何通过weak_ptrs示例再次解决问题."

像这样的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)
Run Code Online (Sandbox Code Playgroud)

回答你问题的第二部分:引用计数在数学上不可能处理周期.因此,a weak_ptr(基本上只是一个简化的版本shared_ptr)不能用于解决循环问题 - 程序员正在解决循环问题.

要解决这个问题,程序员需要了解对象之间的所有权关系,或者如果自然不存在这种所有权,则需要发明所有权关系.

可以更改上面的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.
Run Code Online (Sandbox Code Playgroud)

一个关键问题是:weak_ptr如果程序员无法告知所有权关系并且由于缺乏特权或缺乏信息而无法建立任何静态所有权,那么可以使用它吗?

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

  • 我会选择你的最佳答案.但是嘿..不是我的选择:) +1顺便说一句 (4认同)
  • 如果B :: a是一个weak_ptr,那么什么都不应该取决于它的存在 - 因为它不拥有一个.A :: b在这里是可靠的人. (3认同)

dor*_*ron 47

A shared_ptr围绕原始指针包装引用计数机制.因此,对于每个实例,shared_ptr引用计数增加1.如果两个share_ptr对象相互引用它们将永远不会被删除,因为它们永远不会以引用计数为零结束.

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

这种方法的工作方式是,只要有人想要使用底层对象,weak_ptr就可以使用它来创建shared_ptrfor.但是,如果对象已被删除,shared_ptr则返回a的空实例.由于基础对象的引用计数不会随weak_ptr引用而增加,因此循环引用不会导致基础对象不被删除.

  • 我相信参考控制对象保持"使用"(shared_ptrs)和"Weaks"(weak_ptrs +(使用> 0?1:0))的计数.但这可能是一个恭维细节. (10认同)

new*_*int 17

对于未来的读者.
只是想指出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;  
Run Code Online (Sandbox Code Playgroud)


pet*_*oll 9

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

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