C++ Base class destrutor order problem
Does anyone know any trick I could use to keep the Derived class until the base class destructor have been called?
i.e:
#include <iostream.h>
class Base
{
public:
Base(){ cout<<"Constructor: Base"<<endl;}
virtual ~Base(){ cout<<"Destructor : Base"<<endl;}
};
class Derived: public Base
{
//Doing a lot of jobs by extending the functionality
public:
Derived(){ cout<<"Constructor: Derived"<<endl;}
~Derived(){ cout<<"Destructor : Derived"<<endl;}
};
void main()
{
Base *Var = new Derived();
delete Var;
}
This will result in Derived class to be destroyed, then Base class will be d开发者_Python百科estroyed.
The reason I need something like this is I have a custom Event(signal/slot) class.
The Event class provide an Observer class.
If I define :
class A : public Event::Observer
and then delete an instance of class A, when the ~Observer automatically remove any signal connected to this observer.
But since Class A is destroyed before the Observer, if something on a different thread call a slot on A after ~A and before ~Observer get called. Everything goes to hell...
I can always call the Observer.release method from the ~A, which fix the timing issue. But it was cleaner if I wouldnt need to.
Any ideas?
You definitely don't want to change destruction order, which is good, because you can't.
What you really want to do is to dispose/disconnect/shutdown the Observer.
What I would do is add this to your Event::Observer class:
void Event::Observer::Shutdown()
{
if(!isShutdown)
{
//Shut down any links to this observer
isShutdown = true;
}
}
void ~Event::Observer()
{
Shutdown();
//rest of Event::Observer destruction
}
and then:
~A()
{
Shutdown();
//clean up any other A resources
}
If you did something like IDisposable suggested by David, that would work too -- just call Observer::Dispose()
in your destructor for class A.
My code all assumes that you have only a single thread accessing these objects. Thread synchronization is an entirely separate subject.
Destructors work as they are expected to do and you should not touch them (actually, you can't change the calling order). As for your task - you need proper threads synchronization. As a simplest solution: unsubscribe your observer before deleting it.
I suggest you either implement reference counting or an IDisposable interface and use it as a convention amongst your clients. Whether or not you call Observer::Release()
in your A::dtor()
, YOu're talking about having a different thread come in and call a method on an object that is being destroyed. That is definitely a race condition, you should never have code from another thread asynchronously executing on a method on an object that is being destroyed.
With reference counting, the event subscriber objects don't get deleted until they are unregistered. With an IDisposable pattern, you make sure to remove any references to an object before the destructor is called. Either could be appropriate depending on your architecture.
Base class destructor always gets called after derived class destructor. You mustn't call any of object's methods from other threads after object's destructor begins execution (regardless of whether it has a base class or not). I'd suggest using a new class that works as a container for class Base
instances and implements a safe access to Base
's objects (you probably need to use one of synchronization objects to implement id).
You cannot change the order of destruction in an inheritance relationship and that's good (what would a Derived
be once its Base
was destroyed?. However, you can change the relationship.
For example you could always wrap Derived
in a class (template) which calls release()
first before destroying Derived
.
It's a typical race condition, it's just that this one is glaring.
There are obviously several methods at your disposal. You could for example have a mutex in the Base
class and lock it as soon as you enter the destructor of the most derived class... however as you noted it's difficult.
I would suggest making the destructor protected
(for each class in the hierarchy) and have a method to invoke for destruction (objects cannot be allocated on the stack any longer), then overload delete
for the Base
class, and have it unregister your object first before destroying it by calling this special method.
The only issue is that anyone not respecting the make your destructor protected bit will screw things up.
On the other hand, you could also NOT use inheritance, and prefer composition instead. This would allow you to control the destruction order of the various attributes.
精彩评论