The trick is that std::shared_ptr
performs type erasure. Basically, when a new shared_ptr
is created it will store internally a deleter
function (which can be given as argument to the constructor but if not present defaults to calling delete
). When the shared_ptr
is destroyed, it calls that stored function and that will call the deleter
.
A simple sketch of the type erasure that is going on simplified with std::function, and avoiding all reference counting and other issues can be seen here:
template <typename T>
void delete_deleter( void * p ) {
delete static_cast<T*>(p);
}
template <typename T>
class my_unique_ptr {
std::function< void (void*) > deleter;
T * p;
template <typename U>
my_unique_ptr( U * p, std::function< void(void*) > deleter = &delete_deleter<U> )
: p(p), deleter(deleter)
{}
~my_unique_ptr() {
deleter( p );
}
};
int main() {
my_unique_ptr<void> p( new double ); // deleter == &delete_deleter<double>
}
// ~my_unique_ptr calls delete_deleter<double>(p)
When a shared_ptr
is copied (or default constructed) from another the deleter is passed around, so that when you construct a shared_ptr<T>
from a shared_ptr<U>
the information on what destructor to call is also passed around in the deleter
.