Однажды у нас было интервью с очень опытным разработчиком C ++, который не смог ответить на следующий вопрос: необходимо ли вызывать деструктор базового класса из деструктора производного класса в C ++?
Очевидно, что ответ нет, C ++ в любом случае автоматически вызовет деструктор базового класса . Но что, если мы попытаемся сделать звонок? На мой взгляд, результат будет зависеть от того, можно ли вызвать деструктор базового класса дважды, не вызывая ошибочного поведения.
Например, в этом случае:
class BaseSafe {
public:
~BaseSafe()
{
}
private:
int data;
};
class DerivedSafe {
public:
~DerivedSafe()
{
BaseSafe::~BaseSafe();
}
};
все будет хорошо - деструктор BaseSafe
можно вызвать дважды безопасно и программа будет работать нормально.
Но в этом случае:
class BaseUnsafe {
public:
BaseUnsafe()
{
buffer = new char[100];
}
~BaseUnsafe ()
{
delete[] buffer;
}
private:
char* buffer;
};
class DerivedUnsafe {
public:
~DerivedUnsafe ()
{
BaseUnsafe::~BaseUnsafe();
}
};
явный вызов будет работать нормально, но тогда неявный (автоматический) вызов деструктора вызовет двойное удаление и неопределенное поведение.
Похоже, что легко избежать UB во втором случае. Просто установите buffer
в нулевой указатель после delete[]
.
Но поможет ли это? Я имею в виду, что деструктор должен запускаться только один раз на полностью сконструированном объекте, поэтому оптимизатор может решить, что установка buffer
в нулевой указатель не имеет смысла, и исключить этот код, выставляющий программу на двойное удаление.
Разрешено ли это делать компилятору?