enable_shared_from_this 必须是第一个碱基 class 吗?
Must enable_shared_from_this be the first base class?
我的 class 继承自多个基础,其中之一是 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
所在的存储仍然存在吗?
如果你创建一个 C 类型的对象 c,通过继承基数 A、B 和一个引用计数器 enable_shared_from_this<T>
,首先为整个结果对象分配内存,包括一般的基数和基数 enable_shared_from_this<T>
。在最后一个所有者 (a.k.a. shared_ptr) 放弃所有权之前,该对象不会被销毁。那时~enable_shared...,~B和~A将在~C之后运行。直到最后一个析构函数 ~A 是 运行 之后,完整分配的内存仍然保证存在。 ~A为运行后,一举释放了完整的对象内存。
所以回答你的问题:
When ~A() and ~B() run, can I be sure that the storage where C lived is still present?
是的,虽然您不能合法访问它,但您为什么需要知道?你想避免哪个问题?
When ~A() and ~B() run, can I be sure that the storage where C lived is still present?
否,与基数 类 的顺序无关。即使使用(或不使用)enable_shared_from_this 也无关紧要。
当 C 对象被销毁时(无论如何发生),~C()
将在 之前被调用 ~A()
和 ~B()
,因为这就是基本析构函数的工作方式。如果您尝试 "reconstruct" 基类析构函数中的 C 对象并访问其中的字段,这些字段将已经被销毁,因此您将得到未定义的行为。
When ~A()
and ~B()
run, can I be sure that the storage where C
lived
is still present?
当然可以!很难使用试图释放自己内存(它所在的内存)的基 class。我不确定它是否正式合法。
实现不会这样做:当 shared_ptr<T>
被破坏或重置时,T
的共享所有权的引用计数 (RC) 会减少(原子地);如果递减到0,则启动T
的destruction/deletion。
然后 weak-owners-or-T-exists 计数(原子地)递减,因为 T
不再存在:我们需要知道我们是否是最后一个对控制块感兴趣的实体;如果减量给出非零结果,则意味着某些 weak_ptr
存在控制块的份额(可能是 1 份额,或 100%)所有权,他们现在负责重新分配。
无论哪种方式,对于最后的共同所有者,原子递减在某个时候将以零值结束。
这里没有线程,没有非确定性,显然最后的weak_ptr<T>
在C
的销毁过程中被销毁了。 (你的问题中不成文的假设是没有其他 weak_ptr<T>
被保留。)
破坏总是按照精确的顺序发生。 控制块用于销毁,因为没有人shared_ptr<T>
知道(通常)调用(可能不同的)最派生class的哪个(可能非虚拟)析构函数. (控制块还知道 而不是 在 make_shared
的共享计数达到零时取消分配内存。)
实现之间唯一的实际差异似乎是关于内存栅栏的精细细节以及在常见情况下避免某些原子操作。
我的 class 继承自多个基础,其中之一是 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
所在的存储仍然存在吗?
如果你创建一个 C 类型的对象 c,通过继承基数 A、B 和一个引用计数器 enable_shared_from_this<T>
,首先为整个结果对象分配内存,包括一般的基数和基数 enable_shared_from_this<T>
。在最后一个所有者 (a.k.a. shared_ptr) 放弃所有权之前,该对象不会被销毁。那时~enable_shared...,~B和~A将在~C之后运行。直到最后一个析构函数 ~A 是 运行 之后,完整分配的内存仍然保证存在。 ~A为运行后,一举释放了完整的对象内存。
所以回答你的问题:
When ~A() and ~B() run, can I be sure that the storage where C lived is still present?
是的,虽然您不能合法访问它,但您为什么需要知道?你想避免哪个问题?
When ~A() and ~B() run, can I be sure that the storage where C lived is still present?
否,与基数 类 的顺序无关。即使使用(或不使用)enable_shared_from_this 也无关紧要。
当 C 对象被销毁时(无论如何发生),~C()
将在 之前被调用 ~A()
和 ~B()
,因为这就是基本析构函数的工作方式。如果您尝试 "reconstruct" 基类析构函数中的 C 对象并访问其中的字段,这些字段将已经被销毁,因此您将得到未定义的行为。
When
~A()
and~B()
run, can I be sure that the storage whereC
lived is still present?
当然可以!很难使用试图释放自己内存(它所在的内存)的基 class。我不确定它是否正式合法。
实现不会这样做:当 shared_ptr<T>
被破坏或重置时,T
的共享所有权的引用计数 (RC) 会减少(原子地);如果递减到0,则启动T
的destruction/deletion。
然后 weak-owners-or-T-exists 计数(原子地)递减,因为 T
不再存在:我们需要知道我们是否是最后一个对控制块感兴趣的实体;如果减量给出非零结果,则意味着某些 weak_ptr
存在控制块的份额(可能是 1 份额,或 100%)所有权,他们现在负责重新分配。
无论哪种方式,对于最后的共同所有者,原子递减在某个时候将以零值结束。
这里没有线程,没有非确定性,显然最后的weak_ptr<T>
在C
的销毁过程中被销毁了。 (你的问题中不成文的假设是没有其他 weak_ptr<T>
被保留。)
破坏总是按照精确的顺序发生。 控制块用于销毁,因为没有人shared_ptr<T>
知道(通常)调用(可能不同的)最派生class的哪个(可能非虚拟)析构函数. (控制块还知道 而不是 在 make_shared
的共享计数达到零时取消分配内存。)
实现之间唯一的实际差异似乎是关于内存栅栏的精细细节以及在常见情况下避免某些原子操作。