Почему оператор удаления должен быть статическим?
Я нашел этот один вопрос, задающий одно и то же,, однако была отреагирована только на "новую" часть, поэтому здесь идет снова.
Почему оператор удаления должен быть статическим? Как-то это не имеет смысла. Новый оператор имеет смысл, так же как конструктор не может быть виртуальным, и новый оператор не может быть. Однако деструктор может (и должен) быть виртуальным, когда вы используете наследование, чтобы разрешить уничтожение используемых объектов (посредством полиморфизма) в качестве базового класса.
Я понимаю, что, когда вызывается оператор delete, объект уже уничтожен, поэтому нет 'this'. Тем не менее, по-прежнему имеет смысл, используя те же рассуждения, что и с виртуальным деструктором, чтобы оператор удаления соответствовал новому оператору, который создал объект.
Это то, что я имею в виду
class A
{
public:
virtual ~A() {}
};
class B : public A
{
public:
void* operator new (size_t sz);
void operator delete (void* ptr, size_t sz);
};
теперь, если мы делаем
A *ptr = new B();
delete ptr; // <-- fail
Должен был вызываться оператор удаления (по умолчанию), поскольку он статический и неизвестный (для чего-то, кроме тривиального случая здесь) во время компиляции, который является оператором delete-operator.
Тем не менее, я сделал небольшую тестовую программу с приведенным выше кодом (просто malloc/free в операторах new/delete и print statement в delete) и скомпилировал его с помощью g++. Запуск его довольно неожиданно произвел вывод в операторе удаления B.
Мой (реальный) вопрос: есть ли какая-то неявная "виртуальность" для оператора удаления? Является ли оно только статичным в смысле отсутствия этого указателя? Или это просто функция g++?
Я начал изучать спецификацию на С++, но должен признать, что я был немного ошеломлен этим, поэтому любая помощь была оценена.
Ответы
Ответ 1
Ответ в языковых правилах действительно находится в 12.5 [class.free].
Если вы удаляете указатель на базовый класс, деструктор должен быть виртуальным или вы получаете поведение undefined. В противном случае реализация должна определить динамический тип удаляемого объекта.
12.5/4 говорит, что когда delete
не имеет префикса ::
, тогда функция освобождения определяется путем поиска delete
в контексте виртуального деструктора динамического типа. Это обеспечивает виртуальный поиск, хотя operator delete
всегда является функцией члена static
.
Распределение и освобождение сырья происходит концептуально вне времени жизни объекта, поэтому к тому времени, когда должна быть вызвана функция освобождения, больше нет объекта для предоставления виртуального механизма поиска, но правила поиска гарантируют, что operator delete
имеет динамический (виртуальный!) механизм поиска. Это означает, что удаление оператора может разумно быть static
, не теряя прикосновения к динамическому типу исходного объекта.
Ответ 2
Оператор delete
предназначен для освобождения памяти только, а память освобождается для самого производного объекта класса в целом - в одном действии - точно так же, как и с оператором new
, он выделяется для всего самого производного class object - объект класса, переданный как аргумент в конструкцию new Class
.
Вот почему, когда вы делаете delete ptr;
, оператор delete
всегда вызывается только один раз для фактического наиболее производного класса объекта, который удаляется, а данные о том, что это класс, выводится либо из таблицы vtable, если виртуальный деструктор присутствует или тип указателя, если нет виртуального деструктора. Вот почему неявная виртуальность оператора delete
- вся виртуальность заканчивается в точке вызова деструктора.