It\'s widely known that you can use a shared_ptr
to store a pointer to an incomplete type, as long as the pointer can be deleted (with well-defined behaviour) durin
Question: Where is this required?
If it wasn't required the destructor would have undefined behaviour, and the standard is not in the habit of requiring undefined behaviour :-)
If you meet the preconditions of the constructor, then the destructor will not invoke undefined behaviour. How the implementation ensures that is unspecified, but you can assume it gets it right, and you don't need to know how. If the implementation wasn't expected to Do The Right Thing then the destructor would have a precondition.
(Or am I just too nit-picky and it's obvious somehow that the implementations are required to use a "owner object"?)
Yes, there has to be some additional object created to own the pointer, because the reference counts (or other bookkeeping data) must be on the heap and not part of any specific shared_ptr
instance, because it might need to out-live any specific instance. So yes, there is an extra object, which owns the pointer, which you can call an owner object. If no deleter is supplied by the user then that owner object just calls delete
. For example:
template<typename T>
struct SpOwner {
long count;
long weak_count;
T* ptr;
virtual void dispose() { delete ptr; }
// ...
};
template<typename T, typename Del>
struct SpOwnerWithDeleter : SpOwner<T> {
Del del;
virtual void dispose() { del(this->ptr); }
// ...
};
Now a shared_ptr
has a SpOwner*
and when the count drops to zero it invokes the virtual function dispose()
which either calls delete
or invokes the deleter, depending on how the object was constructed. The decision of whether to construct an SpOwner
or an SpOwnerWithDeleter
is made when the shared_ptr
is constructed, and that type is still the same when the shared_ptr
is destroyed, so if it needs to dispose of the owned pointer then it will Do The Right Thing.