Если base *b
, но *b
на самом деле derived
, тогда delete b;
- это мгновенный UB, потому что base::~base()
не virtual
. По сути, вы пытаетесь удалить base
часть *b
(указанную типом *b
, который равен base
), но, поскольку деструктор не является виртуальным, вы забыли уничтожить derived
часть первая. Это приводит к ужасным вещам (вероятно, к некоторому повреждению стека?). Исправьте это:
struct base {
virtual ~base() = default;
};
Кроме того, Layer::Layer(Layer const&)
(неявно определенный конструктор копирования) не работает, поскольку он дублирует указатели base*
из аргумента. Этот конструктор копирования вызывается, когда std::vector<Layer>
необходимо изменить размер своего хранилища, что влечет за собой выделение нового блока непрерывной памяти и создание новых Layer
из старых, а затем уничтожение старых. За исключением а) Layer
не имеет конструктора перемещения (объявленный пользователем деструктор предотвращает его генерацию), поэтому «перемещение» Layer
s просто копирует их, а б) копирование Layer
s концептуально некорректно, так как когда один Layer
будет уничтожен, он будет delete
всех своих base
с, а затем другой Layer
попытается удалить их позже. Отключите Layer
копирование и запишите его ход.
struct Layer {
std::vector<base*> effects;
Layer() = default;
Layer(Layer const&) = delete;
Layer(Layer&&) = default;
Layer &operator=(Layer const&) = delete;
Layer &operator=(Layer &&other) {
std::swap(this->effects, other.effects);
// what used to be this->effects will be deleted when other is destroyed
return *this;
}
~Layer() {
for(int ii = 0; ii < effects.size(); ii++) {
std::cout << "called effect deleter\n"; // endl is usually unnecessary, and "\n" is portable
delete effects[ii];
}
}
};
Мораль такова: используйте умные указатели:).
struct effect_deleter {
void operator()(base *b) {
std::cout << "called effect deleter\n";
delete b;
}
};
struct Layer {
std::vector<std::unique_ptr<base, effect_deleter>> effects;
// 3 constructors, 2 operator=s, and 1 destructor
// all doing the right thing, "for free"
// "Rule of 5" becomes "Rule of 0"
};
int main() {
std::vector<Layer> layers;
for(int i = 0; i < 10; i++) {
layers.emplace_back(); // using push_back(Layer()) constructs a temporary Layer, then constructs the actual Layer in the vector by moving from the temporary; emplace_back just passes the arguments (here nothing) on to the constructor of Layer
layers[i].effects.emplace_back(new derived()); // similar
std::cout << i << "\n";
}
}