8

我的类继承自多个基础,其中之一是std::enable_shared_from_this. 一定是第一垒吗?

假设以下示例代码:

struct A { ~A(); };
struct B { ~B(); };
struct C : A, B, std::enable_shared_from_this<C> {};

std::make_shared<C>(); 

~A()~B()运行时,我可以确定C居住的存储仍然存在吗?

4

3 回答 3

5

当 ~A() 和 ~B() 运行时,我可以确定 C 所在的存储仍然存在吗?

不,基类的顺序无关紧要。即使使用(或不使用) enable_shared_from_this 也是无关紧要的。

当 C 对象被销毁时(无论如何发生),~C()将在 and 之前调用,因为是基本析构函数的工作方式。如果您尝试在基析构函数中“重构”C 对象并访问其中的字段,则这些字段将已被销毁,因此您将获得未定义的行为。~A()~B()

于 2020-03-16T03:04:47.533 回答
1

~A()~B()运行时,我可以确定C居住的存储仍然存在吗?

当然!很难使用试图释放自己的内存(它所在的内存)的基类。我不确定它是否正式合法。

实现不这样做:当 ashared_ptr<T>被破坏或重置时,共享所有权的引用计数(RC)T减少(原子地);如果减量达到 0,则T开始销毁/删除。

然后弱所有者或 T 存在计数减少(原子地),因为T不再存在:我们需要知道我们是否是最后一个对控制块感兴趣的实体;如果减量结果为非零,则表示weak_ptr存在控制块的份额(可能是 1 份额,或 100%)所有权,他们现在负责解除分配。

无论哪种方式,对于最后一个共同所有者,原子减量最终都会以零值结束。

这里没有线程,没有非确定性,显然最后一个weak_ptr<T>C. (您问题中的不成文假设是没有weak_ptr<T>保留其他内容。)

破坏总是按照这个确切的顺序发生。控制块用于销毁,因为shared_ptr<T>(通常)不知道要调用(可能不同的)大多数派生类的哪个(可能是非虚拟的)析构函数。(控制块也知道在共享计数达到零时释放内存make_shared。)

实现之间唯一实际的变化似乎是关于内存栅栏的细节以及在常见情况下避免一些原子操作。

于 2020-03-18T04:10:55.550 回答
-1

如果您通过从 base 继承来创建 C 类型的对象 c,其基数为 A、B 和引用计数器enable_shared_from_this<T>,首先为整个结果对象分配内存,包括一般的基数和基数enable_shared_from_this<T>。在最后一个所有者(又名 shared_ptr)放弃所有权之前,该对象不会被破坏。那时 ~enable_shared...,~B 和 ~A 将在 ~C 之后运行。在最后一个析构函数 ~A 运行之前,仍然保证完整分配的内存在那里。~A 运行后,一举释放整个对象内存。所以回答你的问题:

当 ~A() 和 ~B() 运行时,我可以确定 C 所在的存储仍然存在吗?

是的,虽然你不能合法地访问它,但你为什么需要知道呢?你想避免哪个问题?

于 2020-03-15T22:09:27.423 回答