Это относится ко второму вопросу об объявлении виртуального деструктора для абстрактного базового класса (например, по крайней мере одна функция-член является чистой виртуальной). Вот пример реального мира компилятора LLVM clang ++, улавливающего потенциальную проблему. Это произошло с версией инструментов командной строки, поставляемой Apple Developer для операционной системы Mac OS X Mavericks.
Предположим, что у вас есть коллекция производных классов, которые в конечном итоге имеют родительский объект с абстрактным базовым классом для определения общего интерфейса. Затем необходимо иметь контейнер хранения, такой как вектор, который намеренно объявлен для хранения указателя на абстрактный базовый класс для каждого элемента. Позже, следуя хорошей технической практике, элементы контейнера должны быть «удалены», а память возвращается в кучу. Самый простой способ сделать это - пересечь векторный элемент по элементу и вызвать операцию удаления на каждом из них.
Ну, если абстрактный базовый класс не объявляет деструктор как виртуальный, компилятор clang ++ дает дружественное предупреждение о вызове не виртуального деструктора в абстрактном классе. Имейте в виду, что на самом деле только производные классы выделяются из кучи с помощью оператора new. Тип указателя производного класса из отношения наследования действительно представляет собой абстрактный тип базового класса (например, отношение is-a).
Если деструктор абстрактного базового класса не является виртуальным, то как будет вызываться дескриптор правильного производного класса для освобождения памяти? В лучшем случае компилятор знает лучше (по крайней мере, потенциально с C++ 11) и делает это возможным. Если опция -Wall компилятора включена, тогда должно появиться предупреждение о компиляции. Однако, в худшем случае, деструктор производного класса никогда не достигается, и память никогда не возвращается в кучу. Следовательно, в настоящее время происходит утечка памяти, которая может быть очень сложной для отслеживания и исправления. Все, что потребуется, - это однократное добавление «виртуального» к декларации деструктора абстрактного базового класса.
Пример кода:
class abstractBase
{
public:
abstractBase() { };
~abstractBase() { };
virtual int foo() = 0;
};
class derived : abstractBase
{
public:
derived() { };
~derived() { };
int foo() override { return 42; }
};
//
// Later on, within a file like main.cpp . . .
// (header file includes are assumed to be satisfied)
//
vector<abstractBase*> v;
for (auto i = 0; i < 1000; i++)
{
v.push_back(new derived());
}
//
// do other stuff, logic, what not
//
//
// heap is running low, release memory from vector v above
//
for (auto i = v.begin(); i < v.end(); i++)
{
delete (*i); // problem is right here, how to find the derived class' destructor?
}
Чтобы устранить эту потенциальную утечку памяти, абстрактный базовый класс должен объявить его деструктор как виртуальные. Больше ничего не требуется. Относящийся базовый класс теперь становится следующим:
class abstractBase
{
public:
abstractBase() { };
virtual ~abstractBase() { }; // insert virtual right here
virtual int foo() = 0;
}
Обратите внимание, что абстрактный базовый класс имеет в настоящее время пустые конструкторы и тела деструктора. Как говорилось в Lightness, компилятор создает конструктор по умолчанию, деструктор и конструктор копирования для абстрактного базового класса (если он не определен инженером). Настоятельно рекомендуется просмотреть любые выпуски языка программирования C++ создателем C++, Bjarne Stroustrup, для получения более подробной информации об абстрактных базовых классах.
'= 0;' разрешено выполнять только виртуальную функцию. – billz
Это не C++. –
@billz и гонки с легкостью на орбите: исправлено – user1911091