How std::enable_shared_from_this Prevents Double Deletion
In C++, managing object lifetimes with std::shared_ptr
relies on a shared control block that tracks reference counts. When a
member function attempts to create a new std::shared_ptr
directly from the raw this pointer, it mistakenly creates a
second, independent control block for the same underlying object,
inevitably leading to undefined behavior and double deletion. Inheriting
from std::enable_shared_from_this prevents this fatal flaw
by enabling an object to produce new std::shared_ptr
instances that alias the existing control block, ensuring accurate
reference counting across the program.
The Problem: Naive Pointer Wrapping
When an object is managed by a std::shared_ptr, memory
management is handled by an allocated control block containing the
reference counts (strong and weak) alongside the deleter.
Consider what happens if an object tries to pass ownership of itself
using the this pointer:
struct Bad {
std::shared_ptr<Bad> get_ptr() {
return std::shared_ptr<Bad>(this); // Creates a new control block!
}
};
int main() {
auto original = std::make_shared<Bad>();
auto duplicate = original->get_ptr();
}In this scenario:
std::make_shared<Bad>()allocates theBadobject and Control Block A (use count: 1).- Calling
get_ptr()invokes thestd::shared_ptrconstructor with a rawBad*. The constructor has no knowledge of Control Block A, so it allocates Control Block B (use count: 1). - When
duplicategoes out of scope, Control Block B decrements to 0 and deletes theBadinstance. - When
originalsubsequently goes out of scope, Control Block A decrements to 0 and attempts to delete the already-freedBadinstance. This causes a double deletion runtime crash.
The Mechanism of std::enable_shared_from_this
std::enable_shared_from_this<T> solves this
problem using the Curiously Recurring Template Pattern (CRTP).
Internally, the base class contains a private
std::weak_ptr<T> member, often referred to as
weak_this.
1. Internal Initialization
When a std::shared_ptr<T> is constructed with a
raw pointer to an object that derives from
std::enable_shared_from_this<T>, the constructor
detects the inheritance via template specialization. It initializes the
internal weak_ptr to point to the newly created control
block without incrementing the strong reference count.
2. Safe Retrieval via
shared_from_this()
Instead of wrapping this into a new
std::shared_ptr, member functions invoke the
shared_from_this() method provided by the base class:
struct Good : public std::enable_shared_from_this<Good> {
std::shared_ptr<Good> get_ptr() {
return shared_from_this(); // Reuses existing control block
}
};
int main() {
auto original = std::make_shared<Good>(); // Initializes internal weak_ptr
auto duplicate = original->get_ptr(); // Increments Control Block A use count to 2
}Under the hood, shared_from_this() operates by
constructing a std::shared_ptr from the internal
weak_ptr:
return std::shared_ptr<T>(this->weak_this);This operation hooks directly into the existing control block,
incrementing its strong reference count rather than generating an
isolated one. Both original and duplicate now
coordinate through the exact same reference counter, preventing
premature destruction.
Crucial Usage Rules
- Pre-existing Ownership Required:
shared_from_this()can only be called if the object is already owned by a validstd::shared_ptr. If called on a stack-allocated instance or a raw heap pointer that was never assigned to astd::shared_ptr, the internalweak_ptrremains uninitialized. In C++17 and later, callingshared_from_this()in this state throwsstd::bad_weak_ptr. - Constructors Cannot Use
shared_from_this(): During the execution of an object's constructor, the enclosingstd::shared_ptrhas not yet finished being created. Therefore, the internalweak_ptris not yet bound to a control block. Attempting to callshared_from_this()within a constructor will triggerstd::bad_weak_ptr.