shared_ptr和weak_ptr的区别
我正在阅读Scott Meyers的“Effective C ++”一书。 有人提到, tr1::shared_ptr
和tr1::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_ptr
和shared_ptr
唯一不同的是, weak_ptr
允许在实际对象被释放之后保留引用计数器对象。 因此,如果你在std::set
保存了很多的shared_ptr
,那么如果实际的对象足够大的话它将占用大量的内存。 这个问题可以通过使用weak_ptr
来解决。 在这种情况下,您必须确保存储在容器中的weak_ptr
在使用之前不会过期。