What SergeyA says is totally correct, for those who want to see the real code,
here is the shared_ptr implementation from LLVM (I only show part of the code which are relevant for explaination):
template<class _Tp>
class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr
{
//Constructor taking a pointer
template<class _Yp, class = _EnableIf<
_And<__compatible_with<_Yp, _Tp>>::value>
>
explicit shared_ptr(_Yp* __p) : __ptr_(__p) {
unique_ptr<_Yp> __hold(__p);
typedef typename __shared_ptr_default_allocator<_Yp>::type _AllocT;
typedef __shared_ptr_pointer<_Yp*, __shared_ptr_default_delete<_Tp, _Yp>, _AllocT > _CntrlBlk;
__cntrl_ = new _CntrlBlk(__p, __shared_ptr_default_delete<_Tp, _Yp>(), _AllocT());
__hold.release();
__enable_weak_this(__p, __p);
}
...
//other code omited
}
//Move Constructor
template<class _Tp>
template<class _Yp>
inline
shared_ptr<_Tp>::shared_ptr(shared_ptr<_Yp>&& __r,
typename enable_if<__compatible_with<_Yp, element_type>::value, __nat>::type)
_NOEXCEPT
: __ptr_(__r.__ptr_),
__cntrl_(__r.__cntrl_)
{
__r.__ptr_ = nullptr;
__r.__cntrl_ = nullptr;
}
Step 1:
when you call shared_ptr<Yellowdog>(new Yellowdog());
, first the constructor taking pointer will be called, _Tp is same as _Yp in this case. There a control block is also created, taking _Yp* (so Yellowdog*) as input, and saves it inside. Then the created control block is saved as its virtual interface (type-erased) inside shared_ptr
Step 2:
Then during function return shared_ptr<Yellowdog>
is converted to shared_ptr<Dog>
.
In this case, the move constructor of shared_ptr<Dog>
is called, however, this time _Tp is Dog, _Yp is Yellowdog. so a implicit conversion from Yellowdog* to Dog* is happening for __ptr_
. But for __ctrl_
it is a simple copy, nothing changes.
Step 3:
During destruction, the deallocator from __ctrl_
will get called, and since it has saved Yellowdog* from the begining, it can call its destructor directly, and in turn it calls its parent (Dog) destructor.
Therefore no virtual destructor is needed for Dog in this case.
Deleter
which is copied/transferred with the pointer. – Francoshared_ptr
is magic. :-S – LawsonDeleter
does something similar todelete (Yellowdog*) p;
. You will get UB withreturn shared_ptr<Dog>(new Yellowdog());
though. – Franco