82

我正在阅读 Scott Meyers 的“Effective C++”一书。有人提到有内置指针,它们tr1::shared_ptrtr1::weak_ptr作用类似于内置指针,但它们会跟踪有多少tr1::shared_ptrs指向一个对象。

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

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

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

4

5 回答 5

128

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."

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

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.

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:

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?

The answer is: If ownership among objects is unclear, weak_ptr cannot help. 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).

于 2011-09-19T15:48:49.060 回答
58

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

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

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

于 2011-02-13T14:08:37.023 回答
21

对于未来的读者。
只想指出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;  
于 2013-09-20T03:56:30.220 回答
9

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

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

于 2014-03-28T18:33:17.750 回答
-6

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

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

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

于 2011-11-10T04:00:38.537 回答