Не удаляйте массивы через указатели на базовый класс
Никогда не удаляйте массив с помощью оператора delete[] через указатель на базовый класс. Это неопределенное поведение (Undefined Behavior). Это может приводить к утечками памяти, повреждению кучи и падению программы.
Проблема кроется в арифметике указателей и механизме вызове деструкторов.
Неправильно:
#include <iostream>
class Base {
public:
int x;
virtual ~Base() { std::cout << "~Base\n"; }
};
class Derived : public Base {
public:
int y;
std::string s;
~Derived() override { std::cout << "~Derived\n"; }
};
int main() {
Base* arr = new Derived[3];
delete[] arr; // Проблема
}
Когда вызывается delete[], компилятор должен сделать две вещи:
- Вызвать деструктор для каждого элемента массива.
- Освободить всю выделенную память одним блоком.
Чтобы сделать первое, компилятору нужно знать точное количество элементов и их размер. Для получения количества компилятор использует мета-информацию, которая размещается прямо перед массивов. Но размер он вычислит из параметра шаблона - sizeof(Base), а не sizeof(Derived). Соответственно смещение ко второму и т.д. элементу будет вычислено неверно, а значит деструкторы будут вызваны для некорректных указателей.
В качестве решения рекомендуется использовать std::vector и умные указатели:
std::vector<std::unique_ptr<Base>> vec;
vec.push_back(std::make_unique<Derived>());
vec.push_back(std::make_unique<Derived>());
Если нужна совместимость с Си, то можно использовать массив указателей: