A missing piece to Kerrek SB's answer is how does the shared_ptr
knows the type ?
The answer is that there are 3 types involved:
- the static type of the pointer (
shared_ptr
)
- the static type passed to the constructor
- the actual dynamic type of the data
And shared_ptr
does not know of the actual dynamic type, but knows which static type was passed to its constructor. It then practices type-erasure... but remembers somehow the type. An example implementation would be (without sharing):
template
class simple_ptr_internal_interface {
public:
virtual T* get() = 0;
virtual void destruct() = 0;
}; // class simple_ptr_internal_interface
template
class simple_ptr_internal: public simple_ptr_internal_interface {
public:
simple_ptr_internal(T* p, D d): pointer(p), deleter(std::move(d)) {}
virtual T* get() override { return pointer; }
virtual void destruct() override { deleter(pointer); }
private:
T* pointer;
D deleter;
}; // class simple_ptr_internal
template
class simple_ptr {
template
struct DefaultDeleter {
void operator()(T* t) { delete static_cast(t); }
};
template
using DefaultInternal = simple_ptr_internal>;
public:
template
simple_ptr(Derived* d): internal(new DefaultInternal{d}) {}
~simple_ptr() { this->destruct(); }
private:
void destruct() { internal->destruct(); }
simple_ptr_internal_interface* internal;
}; // class simple_ptr
Note that thanks to this mechanism, shared_ptr
is actually meaningful and can be used to carry any data an properly dispose of it.
Note also that there is a penalty involved with this semantics: the need for indirection required for the type-erasure of the deleter
attribute.