【问题标题】:Will instance of shared_ptr<Base> and shared_ptr<Derived> with same raw pointer share reference count?具有相同原始指针的 shared_ptr<Base> 和 shared_ptr<Derived> 的实例会共享引用计数吗?
【发布时间】:2020-09-25 04:13:36
【问题描述】:

假设我有两个类,BaseDerived,其中 Derived 继承自 Base。现在,假设我执行以下代码:

shared_ptr<Derived> derivedPtr = make_shared<Derived>();
shared_ptr<Base> basePtr = derivedPtr;

derivedPtr 复制到basePtr 是否会导致derivedPtr 的引用计数被更新(使derivedPtr.use_count()basePtr.use_count() 等于2)?或者,由于shared_ptr 的两个实例是不同的类型,这两个实例是否会有一个不共享的单独引用计数(因此derivedPtr.use_count()basePtr.use_count() 等于1)?

【问题讨论】:

  • 除了标准库实现中的错误(非常罕见),这应该很容易测试。你试过了吗?你的尝试告诉你什么?
  • 是的。你甚至可以这样做:struct Foo { double x; double y; }; auto p = make_shared&lt;Foo&gt;(5.0, 10.0); auto py = weak_ptr(shared_ptr(p, &amp;p-&gt;y);) ... 伪代码,因为我现在不在电脑旁。
  • 刚刚测试了这个,确实,引用计数已更新。

标签: c++ c++11 inheritance polymorphism shared-ptr


【解决方案1】:

所以shared_ptr 不仅仅是一个指针和一个引用计数。

它是一个指针和一个指向控制块的指针。该控制块包含强计数、弱计数和销毁函数。

有 3 种方法可以构造 shared_ptr

首先,您可以从原始指针构造它。发生这种情况时,它会分配一个控制块并在其中插入一个“破坏者”函数来破坏原始指针内存 (delete t;)。

其次,你可以使用make_shared。这为控制块和其中的对象分配了一个块空间。然后它将销毁器设置为仅销毁对象,而不回收内存。控制块的析构函数清除两个内存分配。

第三,有别名构造函数。这些共享控制块(以及因此的销毁代码),但具有不同的对象指针。

最常见的别名构造函数是创建指向基址的指针的构造函数,正如您在上面所做的那样。指向基址的指针与您创建它的共享 ptr 不同,但控制块保持不变。因此,每当控制块达到 0 强引用计数时,它就会将该对象作为其原始派生对象销毁。

比较少见的可以用来返回指向成员变量的共享指针,像这样:

struct Bob {
  int x;
};
auto pBob = std::make_shared<Bob>();
pBob->x = 7;
auto pInt = std::shared_ptr<int>( pBob, &(pBob->x) );

现在pInt 是一个指向pBob-&gt;x 的指针,它共享上面两行创建的Bob 的引用计数(我们在上面创建了pBob)。

pBob = {};

现在指向Bob 的最后一个指针消失了,但对象仍然存在,由pInt 的控制块(和强计数)所有权保持活动状态。

那么当我们:

pInt = {};

Bob 终于被释放了。

您在问题中所做的cast-to-base 隐式转换只是这个的一种变体。

第二个别名构造函数也可以用来做极其奇怪的事情,但那是另一个话题了。


shared/weak ptr 是其中一种情况,您似乎可以在不理解它的情况下对其进行“猴子编码”,但根据我的经验,使用共享所有权非常困难,以至于完全理解 shared ptr (a) 比共享更容易所有权,并且 (b) 更容易获得共享所有权。

【讨论】:

  • 这是一个很好的解释。谢谢!
猜你喜欢
  • 1970-01-01
  • 2012-11-04
  • 1970-01-01
  • 2017-08-06
  • 2010-11-24
  • 2012-09-07
  • 1970-01-01
  • 1970-01-01
  • 2017-05-06
相关资源
最近更新 更多