views:

86

answers:

3

I have a question about the following code:

#include <iostream>
#include <boost/scoped_ptr.hpp>

class Interface
{
};

class A : public Interface
{
    public:
        A() { std::cout << "A()" << std::endl; }
        virtual ~A() { std::cout << "~A()" << std::endl; }
};


Interface* get_a()
{
    A* a = new A;
    return a;
}

int main()
{
    {
        std::cout << "1" << std::endl;
        boost::scoped_ptr<Interface> x(get_a());
        std::cout << "2" << std::endl;
    }
    std::cout << "3" << std::endl;
}

It creates the following output:

1
A()
2
3

As you can see, it doesn't call the destructor of A. The only way I see to get the destructor of A being called, is to add a destructor for the Interface class like this:

virtual ~Interface() { }

But I really want to avoid any Implementation in my Interface class and virtual ~Interface() = 0; doesn't work (produces some linker errors complaining about a non existing implementation of ~Interface().

So my question is: What do I have to change in order to make the destructor being called, but (if possible) leave the Interface as an Interface (only abstract methods).

+2  A: 

You must declare the destructor virtual if you want to delete derived-class objects via a pointer to your base class interface type, and that destructor must have an implementation.

You may still declare it pure virtual, though:

class Interface
{
public:
    virtual ~Interface() = 0;
};

inline Interface::~Interface() { }
James McNellis
You can't do this as written - while pure virtual functions can have bodies, you can't have a function definition and `=0` all in one. You have to put a declaration (with no body) but with `=0` in class body, and the actual definition (with a body) outside, properly qualified.
Pavel Minaev
Clever. I learn another new thing.
Martin York
@Pavel: Thanks... I should have learned by now not to use Visual C++ to check syntax :-)
James McNellis
@James: http://www.comeaucomputing.com/tryitout/ is indispensable for every StackOverflow C++ ninja for precisely this reason :)
Pavel Minaev
+1 For having the in-header inline destructor first.
GMan
@Pavel: Or http://codepad.org/
Justin Ardini
Codepad is great, but uses gcc, which, while generally stricter about standard conformance than VC, is still not quite as pedantic as Comeau (though it's good enough for this particular case).
Pavel Minaev
+5  A: 

You must define a virtual destructor in the base class, otherwise you'll get no polymorphic behavior.

And more importantly, you get undefined behavior otherwise; §5.3.5/3:

If the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.

Emphasis mine.


I'd argue the best is this one:

class Interface
{
public:
    virtual ~Interface(void) = 0;
};

inline Interface::~Interface(void) {}

The compiler can easily inline this, unlike a solution where the implementation resides in a source file. (Speaking of which, this solution doesn't even mandate you have one.) It also leaves the class pure virtual.

GMan
The other solution (with function outside the body of the class) does not require a source file - you can declare it right there in the header so long as it's `inline`. There is also a very hypothetical possibility that the compiler will completely elide the vtable for a given class if all its functions are pure virtual, including destructor (there is a potentially observable difference due to vtable switching during construction/destruction) - it's normally so marginal that it's not worth the bother, but I worked with a codebase where we actually needed `__novtable` regularly once...
Pavel Minaev
@Pavel: You're right. So right that I'm changing my post. :P
GMan
Thanks! That really seems to be the best solution for this problem :)
bb-generation
+2  A: 

You need to define a pure virtual version of the Interface destructor, but you need to also define the body of the destructor. This is a sort of weird case in C++ where even though the function is virtual it must be defined because after the A destructor is called, the Instance destructor will also be called.

Thus the correct answer is:

virtual ~Interface() = 0;

And later, in a cpp file:

Interface::~Interface() {}
SoapBox
`Interface` is simple enough that it might be fine to leave that destructor inline.
Billy ONeal