My class inherits from multiple bases, one of which is std::enable_shared_from_this. Must it be the first base?
Suppose the following example code:
struct A { ~A(); };
struct B { ~B(); };
struct C : A, B, std::enable_shared_from_this<C> {};
std::make_shared<C>();
When ~A() and ~B() run, can I be sure that the storage where C lived is still present?
Of course! It would be hard to use a base class that tries to free its own memory (the memory where it resides). I'm not sure it's even formally legal.
Implementations don't do that: when a
shared_ptr<T>is destructed or reset, the reference count (RC) for the shared ownership ofTis decremented (atomically); if it reached 0 in the decrement, then destruction/deletion ofTis started.Then the weak-owners-or-T-exists count is decremented (atomically), as
Tno longer exists: we need to know if we are the last entity standing interested in the control block; if the decrement gave a non zero result, it means someweak_ptrexist that share (could be 1 share, or 100%) ownership of control block, and they are now responsible for the deallocation.Either way, atomic decrement will at some point end up with a zero value, for the last co-owner.
Here there are no threads, no non-determinism, and obviously the last
weak_ptr<T>was destroyed during destruction ofC. (The unwritten assumption in your question being that no otherweak_ptr<T>was kept.)Destruction always happen in that exact order. The control block is used for destruction, as no
shared_ptr<T>knows (in general) which (potentially non virtual) destructor of (potentially different) most derived class to call. (The control block also knows not to deallocate memory on shared count reaching zero formake_shared.)The only practical variation between implementations seems to be about the fine details of memory fences and avoiding some atomic operations in common cases.