2010-07-20 6 views
2

Кто-нибудь знает какой-либо трюк, который я мог бы использовать, чтобы сохранить класс Derived до тех пор, пока не будет вызван деструктор базового класса?Проблема с порядком удаления деструктора класса C++

т.е.:

#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; 
} 

Это приведет к производный класс должны быть уничтожены, то Базовый класс будет уничтожен.

Причина, по которой мне нужно что-то вроде этого, у меня есть собственный класс событий (сигнал/слот).

Класс Event обеспечивает класс Observer.

Если я определяю:

class A : public Event::Observer 

, а затем удалить экземпляр класса А, когда ~ наблюдатель автоматически удалить какой-либо сигнал, подключенный к этому наблюдателю.

Но так как класс A разрушен до наблюдателя, если что-то в другом потоке вызывает слот на A после ~ A и до того, как будет вызван Observer. Все идет к черту ...

Я всегда могу вызвать метод Observer.release от ~ A, который исправляет проблему синхронизации. Но это было чище, если бы мне не нужно было.

Любые идеи?

ответ

4

Вы определенно не хотите изменять порядок уничтожения, что хорошо, потому что вы не можете.

Что вы действительно хотите сделать - это отключить/отключить Observer.

Что я хотел бы сделать, это добавить это к вашему Event :: класс Observer:


void Event::Observer::Shutdown() 
{ 
    if(!isShutdown) 
    { 
     //Shut down any links to this observer 
     isShutdown = true; 
    } 
} 

void ~Event::Observer() 
{ 
    Shutdown(); 
    //rest of Event::Observer destruction 
} 

, а затем:


~A() 
{ 
    Shutdown(); 
    //clean up any other A resources 
} 

Если вы сделали что-то вроде IDisposable предложил Давиду, что будет работать слишком - просто позвоните Observer::Dispose() в ваш деструктор для класса A.

В моем коде все предполагается, что у вас есть только один поток, обращающийся к этим объектам. Синхронизация потоков - это совершенно отдельный предмет.

+0

Похоже, я не могу назвать деструктор Observer первым, поэтому вызов его в каждом деструкторе класса, который использует :: Observer, является следующей лучшей возможностью. –

+0

Uhm ... предположение о том, что существует один поток, безусловно, неверно, поскольку, если в приложении был один поток, то порядок уничтожения был бы неактуальным. В любом случае +1, поскольку это указывает на решение - это должно быть улучшено в реальном коде с безопасностью потоков. –

2

Деструкторы работают так, как они должны делать, и вы не должны прикасаться к ним (на самом деле вы не можете изменить порядок вызова). Что касается вашей задачи - вам нужна правильная синхронизация потоков. Как простейшее решение: отмените подписку на своего наблюдателя перед ее удалением.

2

Я предлагаю вам либо выполнить подсчет ссылок, либо интерфейс IDisposable, и использовать его как соглашение между вашими клиентами. Независимо от того, вызываете ли вы Observer::Release() в своем A::dtor(), вы говорите о том, что в него входит другой поток и вызывается метод на уничтожаемом объекте. Это определенно условие гонки, вы никогда не должны иметь код из другого потока, выполняющий асинхронное выполнение на методе объекта, который уничтожается.

При подсчете ссылок объекты подписчика события не удаляются до тех пор, пока они не будут зарегистрированы. С шаблоном IDisposable вы обязательно удаляете любые ссылки на объект до вызова деструктора. Любой из них может быть соответствующим в зависимости от вашей архитектуры.

0

Деструктор базового класса всегда вызывается после деструктора производного класса. Вы не должны вызывать какие-либо методы объекта из других потоков после запуска деструктора объекта (независимо от того, имеет ли он базовый класс или нет). Я бы предложил использовать новый класс, который работает как контейнер для экземпляров class Base и реализует безопасный доступ к объектам Base (вам, вероятно, нужно использовать один из объектов синхронизации для реализации id).

0

Вы не можете изменить порядок уничтожения в правоотношения наследования, и это хорошо (Что бы Derived быть когда-то его Base был разрушен ?. Однако вы можете изменить отношения.

Например, вы всегда можно обернуть Derived в классе (шаблон), который называет release() первыми перед уничтожением Derived.

0

это типичное состояние гонки, это просто, что это одна вопиющие.

В вашем распоряжении, очевидно, несколько методов. Например, вы можете использовать мьютекс в классе Base и заблокировать его, как только вы введете деструктор самого производного класса ... однако, как вы отметили, это сложно.

Я хотел бы предложить сделать деструктор protected (для каждого класса в иерархии) и есть метод для вызова для уничтожения (объекты не могут быть выделены в стеке больше), то перегрузка delete для Base класса, и иметь его сначала отмените регистрацию своего объекта, прежде чем уничтожить его, вызвав этот специальный метод.

Единственная проблема заключается в том, что любой, кто не уважает , сделает ваш деструктор защищенным бит будет винить вещи.

С другой стороны, вы также НЕ можете использовать наследование и предпочитаете композицию вместо этого. Это позволит вам контролировать порядок уничтожения различных атрибутов.

Смежные вопросы