First thing is that as it stands the code will not work. The destructor of base
must be at the very least protected
(or derived classes be friends of the base). A private
destructor means that the compiler will not allow you to write the destructor for the derived classes. Now, assuming that you have a protected
destructor... (Rembember, if you design a class to be extended, provide either a public virtual destructor or a protected non-virtual!)
All depends on the implementation of the SmartPointer
, in particular std::shared_ptr
(or the boost counterpart boost::shared_ptr
) are able to manage that situation cleanly. The solution performs some sort of partial type erasure of the type for destruction purposes. Basically, the smart pointer has a templated constructor that accepts any pointer that can be assigned to a base
pointer, but because it is templated it knows the concrete type. At that point it stores a synthetic deleter
function that will call the appropriate destructor.
For simplicity, using std::function
:
template <typename T>
void delete_deleter( void * p ) {
delete static_cast<T*>(p);
}
template <typename T>
class shared_pointer {
T * ptr;
std::function<void(void*)> deleter;
public:
template <typename U>
shared_pointer( U* p, std::function<void()> d = delete_deleter<U> )
: ptr(p), deleter(d)
{}
~shared_pointer() {
deleter( ptr ); // call the stored destructor
}
};
The code is for exhibition only, it would have to be tweaked for production (where to store the function
, reference counting...), but it is enough to give you the idea: in the only function where the exact type of the object is known (when creating the smart pointer), you create a wrapper that will call the exact version of the destructor that you need (providing some short of type erasure), then just leave it around and when you need to delete
the object call it instead of the delete
operator.
This can also be used to manage other resources that require calling a special method instead of delete
:
// exhibition only!
shared_pointer<Foo> p( Factory.create(), &Factory::release );
Again there should be quite a lot of work before making this production ready.
Dependency on std::function
which is used to simplify the erasure, can be eliminated from the problem. In the simple case (only memory allocated with new
and freed with delete
is supported in the smart pointer), then just provide a deleter
base class with a single virtual operator()(void*)
, and then refactor the existing delete_deleter
into templated derived classes from deleter
that override operator()(void*)
with the current implementation. If you need to go for the general case (hold any type of resource) it is not worth the effort, just use std::function
or boost::function
.