shared_ptr和weak_ptr的区别

我正在阅读Scott Meyers的“Effective C ++”一书。 有人提到, tr1::shared_ptrtr1::weak_ptr像内置指​​针一样工作,但是它们跟踪有多less个tr1::shared_ptrs指向一个对象。

这被称为参考计数。 这可以很好地防止非循环数据结构中的资源泄漏,但是如果两个或多个对象包含tr1::shared_ptrs以形成一个循环,那么即使循环的所有外部指针都具有循环,循环也可以使对方的引用计数保持在零以上被摧毁。

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

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

一个shared_ptr包裹一个原始指针周围的引用计数机制。 因此,对于shared_ptr的每个实例,引用计数都会增加1。 如果两个share_ptr对象引用了彼此,它们将永远不会被删除,因为它们永远不会以引用计数为零结束。

weak_ptr指向一个shared_ptr但不会增加其引用计数。这意味着即使有weak_ptr引用,仍然可以删除不明的对象。

这样做的方式是weak_ptr可以用来创build一个shared_ptr任何时候想要使用底层对象。 如果对象已经被删除,则返回一个shared_ptr的空实例。 由于基础对象的引用计数不会随着weak_ptr引用而增加,因此循环引用不会导致基础对象不被删除。

让我重复一下你的问题:“我的问题是循环数据结构如何使引用计数在零以上,请在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) 

回答你的问题的第二部分:引用计数在math上不可能处理周期。 因此, 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. 

一个关键的问题是:如果程序员不能说出所有权关系,并且由于缺乏特权或缺乏信息而无法build立任何静态所有权,那么是否可以使用weak_ptr

答案是:如果对象之间的所有权不清楚, weak_ptr 不能帮助。 如果有一个循环,程序员必须find并打破它。 另一种补救方法是使用具有完全垃圾回收function的编程语言(比如:Java,C#,Go,Haskell),或者使用与C / C ++一起工作的保守(不完美)垃圾回收器(如Boehm GC) 。

为未来的读者。
只是想指出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; 

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

weak_ptr的定义是为了使其相对简单而devise的,因此很less有人可以直接使用weak_ptr 。 例如,你不能解除引用它; 无论operator*还是operator->都是为weak_ptr定义的。 你不能通过它访问指向对象的指针 – 没有get()函数。 有一个定义的比较函数,可以将weak_ptrs存储在一个有序的容器中,但仅此而已。

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

基本上,如果所有的shared_ptr(s)都是由make_shared()或者allocate_shared()调用创build的,那么如果你没有其他资源来pipe理内存,你将永远不需要weak_ptr 。 这些函数用对象本身创buildshared_ptr引用计数器对象,同时释放内存。

weak_ptrshared_ptr唯一不同的是, weak_ptr允许在实际对象被释放之后保留引用计数器对象。 因此,如果你在std::set保存了很多的shared_ptr ,那么如果实际的对象足够大的话它将占用大量的内存。 这个问题可以通过使用weak_ptr来解决。 在这种情况下,您必须确保存储在容器中的weak_ptr在使用之前不会过期。