Introduction
We have a device manager that keeps track of currently attached devices. They happen to be USB devices but that is irrelevant. The issue we face is that devices can be attached or removed at any time. The class factory will scan the list of currently attached devices and if found, returns a shared_ptr
to that device. If the device is removed the device manager will remove the object from the list, which will delete the object. This is bad news for any routines currently using a pointer to that object. The object needs to be kept alive until all routines using that device let go. This is an ideal application of the shared_ptr
.
My problem is the usual hierarchy doesn’t work with shared_ptr
.
class Base {};
class DeviceOne : public Base {};
class DeviceTwo : public Base{};
shared_ptr<Base> classfactory( string const &dev ) {};
shared_ptr<DeviceOne> d1 = classfactory( "deviceone");
shared_ptr<DeviceTwo> d2 = classfactory("devicetwo");
shared_ptr<Base> d3 = classfactory("deviceone");
classfactory
has to return a shared_ptr<Base>
. The way I solved the problem was to define a class that keeps the device object alive by protecting the object instance, while allowing convenient access to what it points to. It does add another pointer to overhead, but I thought it was worth it. The extra pointer can be removed. Just modify the operator->()
to return the dynamic_cast
. Since there is a runtime penalty for dynamic_cast
, I chose to only pay for that once, at the cost of the extra pointer memory.
template<typename T>
class PDerrived
{
public:
PDerrived( shared_ptr<Base> p_ )
: b(p_) , pd( dynamic_cast<T *>(p_.get()) ) {}
T *operator->() const { return pd; }
operator bool () const { return pd != nullptr; }
operator shared_ptr<Base> () const { return b; }
private:
shared_ptr<Base> b;
T *pd;
};
PDerrived <deviceOne> d1 = classfactory( "deviceone");
PDerrived <deviceTwo> d2 = classfactory("devicetwo");
PDerrived <Base> p = d1;