Does delete work with pointers to base class?
Do you have to pass delete the same pointer that was returned by new, or can you pass it a pointer to one of the classes base types? For example:
class Base
{
public:
virtual ~Base();
...
};
class IFoo
{
public:
virtual ~IFoo() {}
virtual void DoSomething() = 0;
};
class Bar : public Base, public IFoo
{
public:
virtual ~Bar();
void DoSomething();
...
};
Bar * pBar = new Bar;
IFoo * pFoo = pBar;
delete pFoo;
Of course this is greatly simplified. What I really want to do is create a container full of boost::shared_ptr and pass it to some code that will remove it from the container when it is finished. This code will know nothing of the implementation of Bar or Base, and will rely on the implied delete operator in the shared_ptr destructor to do the right thing.
Can this possibly work? My intuition says no, since the pointers will not have the same address. On the other hand, a dynamic_cast<Bar*> should work, so somewhere the compiler is storing enough information to figure it out.
Thanks for the help, everybody who answered and commented. I already knew the importance of virtual destructors, as shown in my example; after seeing the answer I gave it a little thought, and realized the whole reason for a virtual destructor is this exact scenario. Thus it had to work. I was thrown by the absence of a visible means of converting the pointer back to the original. A little more thinking led me to believe there was an invisible means, and I theorized that the destructor was returning the true pointer for delete to release. Investigating the compiled code from Microsoft VC++ confirmed my suspicion when I saw this line in ~Base:
mov eax, DWORD PTR _this$[ebp]
Tracing the assembler revealed that this was the pointer being passed to the delete function. Mystery solved.
I've fixed the example to add the virtual destructor to IFoo, it was a simple oversight. Thanks again to everyone who pointed it out.
Solution 1:
Yes, it will work, if and only if the base class destructor is virtual, which you have done for the Base
base class but not for the IFoo
base class. If the base class destructor is virtual, then when you call operator delete
on the base class pointer, it uses dynamic dispatch to figure out how to delete the object by looking up the derived class destructor in the virtual function table.
In your case of multiple inheritance, it will only work if the base class you're deleting it through has a virtual destructor; it's ok for the other base classes to not have a virtual destructor, but only if you don't try to delete any derived objects via those other base class pointers.
Solution 2:
This doesn't relate to your given example, but since you mentioned that you're really interested in shared_ptr
's behavior when deleting its owned object, you might be interested in using shared_ptr
's 'deleter'.
If the object owned by the shared_ptr
needs special handling when being deleted, you can specify a 'deleter' for any particular shared_ptr<>
. The deleter is not part of the type, it's an attribute of a shared_ptr<>
instance, so your container of shared_ptr<>
objects could have some objects with different deleters. Here's what the Boost docs say about the shared_ptr<>
deleter:
Custom deallocators allow a factory function returning a
shared_ptr
to insulate the user from its memory allocation strategy. Since the deallocator is not part of the type, changing the allocation strategy does not break source or binary compatibility, and does not require a client recompilation. For example, a "no-op" deallocator is useful when returning ashared_ptr
to a statically allocated object, and other variations allow ashared_ptr
to be used as a wrapper for another smart pointer, easing interoperability.
It would be cleanest if you could modify IFoo
to have a virtual destructor since you're planning to delete objects that are subclasses of it through an IFoo
reference or pointer. But if you're stuck with an IFoo
that cannot be corrected, then if you want to use shared_ptr<IFoo>
in your container, but have it pointing to a Bar
, you could create the shared_ptr
instance with a deleter that performs a downcast to a Bar*
then does the delete operation. Downcasts are considered bad form, but this technique might be something that you could use in a bind.