Sign in to follow this  

Function overriden in derived class not invoked in destructor

This topic is 3660 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've just observed this: Say I have
class Base
{
public:
  ...
  ~Base();
  ...

protected:
  virtual void releaseResources();
};

Base::~Base()
{
  releaseResources();
}

void Base::releaseResources()
{
  // release resources the Base way
}
Then I have
class Derived : public Base
{
public:
  ~Derived();
  ...

protected:
  virtual void releaseResources();
};

Derived::~Derived
{
  // destructor of parent class is implicitly called right
}

void Derived::releaseResources()
{
  // release resources the Derived way
}
Why is it that Derived::releaseResources() is not invoked whenever Derived is destructed? Since releaseResources() is overriden, shouldn't the releaseResources() in ~Base() invoke Derived's releaseResources(). By following the debugger, Derived's releaseResources() never gets invoked. I wanted my child classes to be free of implementing their own destructor by using virtual functions instead. If that's the way C++ works, how do I achieve this? Currently, all of my child classes explicitly have the statements what their parent has in their destructor (i.e. releaseResources()) just so their own implementation of it will be invoked. I just think it's bad.

Share this post


Link to post
Share on other sites
Overridden virtual classes are not called in constructors and destroctors. It's perfectly normal, since when your base class is being destroyed, your derived class no longer exists. Your destructor should clean up after your class, that's what it's meant to be.

Share this post


Link to post
Share on other sites
Quote:
Original post by mazelle
Why is it that Derived::releaseResources() is not invoked whenever Derived is destructed? Since releaseResources() is overriden, shouldn't the releaseResources() in ~Base() invoke Derived's releaseResources(). By following the debugger, Derived's releaseResources() never gets invoked.


It's one of those annoying C++ warts. Since ~Derived has allready been called by the time you get into ~Base, the sanity of calling any of Derived's virtual functions is no longer particularly assured. Because of this, the C++ standard dictates the behavior you're running into -- Base::releaseResources() will be called instead of Derived::releaseResources(). Annoying, I know.

Quote:
I wanted my child classes to be free of implementing their own destructor by using virtual functions instead. If that's the way C++ works, how do I achieve this? Currently, all of my child classes explicitly have the statements what their parent has in their destructor (i.e. releaseResources()) just so their own implementation of it will be invoked. I just think it's bad.


It is a bit of a nasty situation. The best solution is to have your child classes delegate the responsibility to member classes, rather than relying on overloading. The best approach probably depends on the classes themselves, so feel free to give some concrete examples if you'd like some suggestions.

Share this post


Link to post
Share on other sites
I think it's not really annoying. It's perfectly normal.
And is it really that annoying to just write ~Derived() { ReleaseResources(); } ?
Maybe you should use RAII? The most of the time you don't need destructors (except for you resource classes, of course).

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
It's one of those annoying C++ warts.


Actually, it's an issue in pretty much every language with inheritance and destructors: you have a derived portion and a base portion in your object, what order should they be destroyed in? Given that the base may sometimes use the derived functionality and vice versa, both orders can cause this kind of problems. However, since derived-uses-base happens much more often than base-uses-derived, the "destroy derived then base" order is the one that causes the least trouble.

Share this post


Link to post
Share on other sites
The guys are right. And if you think about it, it sounds logical.

But I have some advise too:

Always make the destructor of a class virtual if you want to derive from that class.
Because if you don't, you can get into trouble when using a base pointer holding the derived class. (polymorphism)

Share this post


Link to post
Share on other sites
The problem is that for a certain family of classes, what if I wanted to add some functionality in their constructor/destructor. We can simply do this by updating the constructor/destructor of the Base class (if the way I wanted it to work works).


Base::~Base()
{
doThisStuff();
releaseResources();
doThatStuff();
}


This would imply that the derived classes don't have to know that there's been some added functionality in their constructor/destructor. Classes are a great tool to manage code and to reduce the root of all evil, redundant code. But it seems it's not working the way I wanted it this time (it works in Java).

One workaround is to explicitly invoke the initialization/get-ready-to-be-destroyed code (remove the code in constructor/destructor):


Base* anObject = new AClassInTheFamily();

anObject->loadResources();
// do stuff
anObject->releaseResources();


This way, the functions in derived classes gets invoked. But obviously enough, this introduces some problems. That is you are obligated to call loadResources() and releaseResources() somewhere in your program which is an awkward thing to do. It also becomes a problem when there are errors in between these two calls. You may never get to call releaseResources(). And, you may also forget calling releaseResources().

If you have other suggestions, please post them.

Share this post


Link to post
Share on other sites
Quote:
Original post by mazelle

Base* anObject = new AClassInTheFamily();

anObject->loadResources();
// do stuff
anObject->releaseResources();
This way, the functions in derived classes gets invoked. But obviously enough, this introduces some problems. That is you are obligated to call loadResources() and releaseResources() somewhere in your program which is an awkward thing to do. It also becomes a problem when there are errors in between these two calls. You may never get to call releaseResources(). And, you may also forget calling releaseResources().

If you have other suggestions, please post them.

In any situation where you find yourself doing this (calling an initialisation function after construction, and a termination function prior to destruction), the answer is almost always going to be to move those functions into your constructor/destructor!

class Base
{
public:
...
Base();
virtual ~Base();//Always make your destructor virtual in polymorphic classes!
...
};

Base::Base()
{
// acquire Base resources
}

Base::~Base()
{
// release Base resources
}


class Derived : public Base
{
public:
Derived();
virtual ~Derived();
...
};

Derived::Derived() : Base()
{
// acquire Derived resources
}

Derived::~Derived
{
// release Derived resources
}




Quote:
Classes are a great tool to manage code and to reduce the root of all evil, redundant code. But it seems it's not working the way I wanted it this time (it works in Java).

Seeing as you mention Java, you should probably keep in mind that in C++ inheritance should not be used for code-reuse (i.e. don't use inheritance just to remove redundant code!). That's what composition (aggregation) is for.

Share this post


Link to post
Share on other sites
Hodgman name-dropped composition, I name dropped class members (e.g. composition), and while the abstract way of describing the approach is basically just "use member classes with destructors!!!!111oneoneoneeleventycos(0)", knowing which of the many prexisting tools you should use for this is going to depend on the concrete details. Feel free to supply these with concrete examples.

Share this post


Link to post
Share on other sites

This topic is 3660 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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