【问题标题】:shared_ptr and weak_ptr differencesshared_ptr 和 weak_ptr 的区别
【发布时间】:2011-06-26 10:39:11
【问题描述】:

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

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

这就是tr1::weak_ptrs 的用武之地。

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

【问题讨论】:

  • "weak_ptrs 是如何解决问题的?" 不是。它通过适当的设计解决,不存在所有权周期。

标签: c++ c++11 shared-ptr weak-ptr cyclic-reference


【解决方案1】:

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

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

一个关键的问题是:weak_ptr可以在程序员因为缺乏权限或信息不足而无法分辨所有权关系并且无法建立任何静态所有权的情况下使用吗?

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

【讨论】:

  • 但是B::a 的所有使用现在必须准备好弱引用已死。如果一定不是这种情况,那么这意味着weak_ptr 不是一个合适的工具。
  • 如果 B::a 是一个 weak_ptr,那么什么都不应该依赖于它的存在——因为它不拥有 a。 A::b 是这里可靠的人。
  • 我会选择你的作为最佳答案。但是,嘿..不是我的选择:) +1 btw
【解决方案2】:

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

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

其工作方式是weak_ptr 可用于创建shared_ptr 以供任何人想要使用底层对象时使用。但是,如果对象已被删除,则返回 shared_ptr 的空实例。由于weak_ptr 引用不会增加基础对象的引用计数,因此循环引用不会导致基础对象不被删除。

【讨论】:

  • 我相信参考控制对象保留了“Uses”(shared_ptrs)和“Weaks”(weak_ptrs + (Uses > 0 ? 1 : 0))的计数。但这可能是一个实施细节。
【解决方案3】:

供未来的读者阅读。
只想指出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_ptr 是只用于循环引用还是缓存?
【解决方案4】:

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

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

【讨论】:

    【解决方案5】:

    以上所有答案都是错误的。 weak_ptr 不用于破坏循环引用,它们还有其他用途。

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

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

    【讨论】:

    • "weak_ptr 不用于破坏循环引用" +1 "它们有其他用途。" 但该答案并未解释发布目的,所以 -1
    • "如果所有 shared_ptr(s) 都是由 make_shared() 创建的" 那么你不需要shared_ptrunique_ptr 更合适)。引用计数的价值在于多个对象持有引用。这是通过分配发生的,就像非常好的例子一样。
    猜你喜欢
    • 2011-04-19
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 2019-11-25
    • 2016-07-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多