Sign in to follow this  
HeavyStorm

Virtual Destructor and Templates: A Crazy Doubt

Recommended Posts

So, there I am, sweating and trembling, looking at my IDE screen trying to understand why a nice delete instruction was crashing my program. It was very dark: I was calling delete upon a pointer to a base interface, hoping that it would destroy the derived class. When that didn't happened and windows informed me that there was an error in MY PROGRAM, I couln't believe it. I started blaming windows, Microsoft and I almost committed the sacrilege of cursing Visual Studio. I expected that, if the correct destructor (the derived class' destructor) wasn't called, at least nothing would be done, since the derived class ( I'll present the classes shortly) had no real data, only two pointers to object. That didn't happen. C++ was trying to do something that he shouldn't, I can't even imagine what. So I inserted a fake destructor in my base class, a virtual one, to make sure the right one was called. That's when things got confused in my mind: my compiler wouldn't let me make a pure virtual destructor, which meant that interfaces don't have destructors. But if that is true, when I kill an interface, why doesn't the right destructor is called? Of course, I have a clue... the derived class is a template, and maybe that is interfering with the dynamic binding. For demonstrative purposes only, here are simulacrums of my classes:
[source=C++]
class Interface
{
   void SomeMethod();
   //virutal ~Interface(){}      // uncomment this!
};

template<class T>
class Implementation : public virtual Interface
{
   typedef (void)(T::*pFun0Void)();
   typedef (void)(T::*pFun1Void)(void*);

    Implementation(T* obj, pFun0Void func)
    {
       object = obj;
       funcPointer = func;
    }

    ~Implementation()
    {
        object = NULL;
        funcPointer = NULL;
    }

   union
   {
      pFun0Void a;
      pFun1Void b;
   } funcPointer;

   T* object;

};

int main()
{
   Interface* test 
        = Implementation<SomeClass>(new SomeClass, SomeClass::someMethod);

   delete test;  // Bug if the virtual destructor in Interface is commented.
   return 0;
}



What makes me crazy, is that, although the solution was obvious, Interfaces shouldn't have destructors, so we can't properly delete a Interface? [Edited by - HeavyStorm on March 16, 2006 1:01:33 PM]

Share this post


Link to post
Share on other sites
I don't understand what your problem is, especially since the code snippet you posted isn't even close to being compileable. If you wish to delete a derived class via a pointer-to-base you must have an accessible virtual destructor in your base class. If you wish to make the destructor pure virtual you must still provide an implementation.

Σnigma

Share this post


Link to post
Share on other sites
If you're going to start talking about compiler errors, please tell us the compiler error so we know what's going on.

The biggest error throughout the code is that all the members are private. (The public inheritance does not make private parent methods public; they're still private.) So I'm not sure how anything here could work.

As a start, try changing Interface to this:


class Interface
{
public:
void SomeMethod();
virutal ~Interface() {}
};



And go from there...

Share this post


Link to post
Share on other sites
Quote:
Original post by HeavyStorm
What makes me crazy, is that, although the solution was obvious, Interfaces shouldn't have destructors, so we can't properly delete a Interface?


In C++, interfaces are just classes. All classes have destructors, although they may have been automatically generated by the compiler instead of by yourself. Further, since C++ requires a virtual destrcutor in order to properly delete a derived class through a pointer-to-base class, all interfaces should have a virtual destructor.

It's quirky enough behavior that it has been at least proposed, if not decided upon, for the next revision of C++ ("C++0x") that if a class contains any virtual functions, that the destuctor will automatically become virtual.

Share this post


Link to post
Share on other sites
Quote:
If you wish to make the destructor pure virtual you must still provide an implementation.

Σnigma


Which means that the destructor would not be pure virtual. Actually, there's no problem, I was just trying to understand why does C++ requires a destructor implementation in a interface if you want a virtual destructor.

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
Quote:
Original post by HeavyStorm
What makes me crazy, is that, although the solution was obvious, Interfaces shouldn't have destructors, so we can't properly delete a Interface?


In C++, interfaces are just classes. All classes have destructors, although they may have been automatically generated by the compiler instead of by yourself. Further, since C++ requires a virtual destrcutor in order to properly delete a derived class through a pointer-to-base class, all interfaces should have a virtual destructor.

It's quirky enough behavior that it has been at least proposed, if not decided upon, for the next revision of C++ ("C++0x") that if a class contains any virtual functions, that the destuctor will automatically become virtual.


You got what I was saying. I think that what I missed yesterday was the fact that indeed, there are no real interfaces in C++, so that's why I needed the virtual destructor, and of course I had to implement this destructor, because C++ wouldn't let me create a pure virtual destructor. This revision would be good, since if you have anything virtual in a class, you probably expect it to be inherited and so you will need a virtual destructor.

And I'm sorry for the people who took the time to even look at the code... this wasn't my real implementation (it's eight times larger than this), I just wrote this snippet so that you would understand my doubt.

Thanks a lot people, I'm really sorry to bother you all with stuff like this. I was just curious.

Share this post


Link to post
Share on other sites
Quote:
Original post by HeavyStorm
Which means that the destructor would not be pure virtual. Actually, there's no problem, I was just trying to understand why does C++ requires a destructor implementation in a interface if you want a virtual destructor.

Requiring an implementation does not at all change the fact that the destructor is pure virtual. Any pure virtual function can have an implementation. That implementation can only be invoked by an explicit call. The only difference with the destructor is that the explicit call is implicitly included in the derived class destructor.
#include <iostream>

struct Base
{

virtual ~Base() = 0;
virtual void function() = 0;

};

Base::~Base()
{
std::cout << "Base::~Base()\n";
}

void Base::function()
{
std::cout << "Base::function()\n";
}

struct Derived
:
public Base
{

~Derived()
{
std::cout << "Derived::~Derived()\n";
} // implicit call to Base::~Base()

void function()
{
std::cout << "Derived::function()\n";
Base::function();
}

};

int main()
{
Derived d;
d.function();
}

Try running that and then try seperately commenting out the definitions of the two pure virtual functions and noting the compiler/linker errors.

Σnigma

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
Further, since C++ requires a virtual destrcutor in order to properly delete a derived class through a pointer-to-base class, all interfaces should have a virtual destructor.

It's quirky enough behavior that it has been at least proposed, if not decided upon, for the next revision of C++ ("C++0x") that if a class contains any virtual functions, that the destuctor will automatically become virtual.

That's not true, which is why it isn't standard already. Not all interfaces should have a virtual destructor. The only time a destructor would have to be virtual is if you are to delete children of the type via a pointer to the base. Not all types which have virtual functions are deleted via a pointer to base, which is why you are given the choice.

Share this post


Link to post
Share on other sites
Quote:
Original post by Polymorphic OOP
Quote:
Original post by MaulingMonkey
Further, since C++ requires a virtual destrcutor in order to properly delete a derived class through a pointer-to-base class, all interfaces should have a virtual destructor.

It's quirky enough behavior that it has been at least proposed, if not decided upon, for the next revision of C++ ("C++0x") that if a class contains any virtual functions, that the destuctor will automatically become virtual.

That's not true, which is why it isn't standard already. Not all interfaces should have a virtual destructor. The only time a destructor would have to be virtual is if you are to delete children of the type via a pointer to the base. Not all types which have virtual functions are deleted via a pointer to base, which is why you are given the choice.


"All" and "automaticlly" were a bad choice of words (I suppose). "99.9% of" and "by default" would have been better - (the later of which is what Bjarne Stroustrup proposes - see the final section, under the bullet "Remove embarrassments"). The costs are an added entry for the vtable, and a (possible) vtable lookup on delete - which will hardly be the most expensive component of freeing memory - and that will only be when the compiler can't be certain of the type that it's deleting.

The likelyhood of a non-virtual dtor being an appropriate optimization being as slim as it is (the bottleneck really is freeing objects, and it really has nothing to do with your memory manager?!?), and the bountiful opportunities for added debugging costs that this optimization brings (undefined behavior on delete-through-base), it makes no sense for it to be the default. The fact that it currently is the default reflects on oversight rather than appropriateness. And it is with some reservation that I downgrade my statement from "all" to "99.9%" - chances are, if a vtable lookup there is really all that bad, you've got a bigger problem with far better optimization techniques available which should be focused upon, rather than distracting yourself with this minor optimization.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this