суббота, 21 августа 2010 г.

Интерфейсы и виртуальные деструкторы

Почему деструктор нужно делать виртуальным, описано на многих блогах и форумах (даже линки приводить не буду, гуглите – материала полно).
Но это только вершина айсберга... оказывается, всем интерфейсам нужно добавить виртуальные деструкторы, иначе велик шанс, что деструктор реализации вызван не будет.

Вот почему.

Смотрим на обычный интерфейс и его реализацию:


class IFile
{
public:
virtual void Process() = 0;
};

class FileImplementation : public IFile
{
public:
FileImplementation()
{
// тут открываем файл
}

virtual ~FileImplementation()
{
// а тут закрываем файл
}

virtual void Process()
{
// какие-то действия
}
};


Что мы видим? Стандартная имплементация в стиле RAII.
Создаем экземпляр имплементирующего класса и проходимся отладчиком:


{
std::auto_ptr<FileImplementation> file(new FileImplementation());
file->Process();
}


При выходе из области видимости деструктор вызовется и файл закроется.
А теперь попробуем воспроизвести Dependency Injection:


{
std::auto_ptr<IFile> file(new FileImplementation());
file->Process();
}


И видим, что деструктор FileImplementation не будет вызван!
Объяснение простое. Интеллектуальный указатель будет удалять экземпляр IFile, который ничего не знает о своей реализации, кроме виртуальных методов. И про деструктор у имплементации он тоже ничего не знает, даже не смотря на то, что он – виртуальный. Причина: он становится виртуальным уже НИЖЕ по архитектуре.

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

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


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


Мораль сей басни такова. Добавьте во все классы виртуальный деструктор, хотя бы тривиальный. И будут они жить долго и счастливо...

Читать дальше...