практическая необходимость виртуального деструктора в конкретном случае - PullRequest
0 голосов
/ 06 декабря 2010

C ++ 03 5.3.5.3

В первом варианте (удалить объект), если статический тип операнд отличается от его динамического тип, статический тип должен быть базовым класс динамического типа операнда и статический тип должен иметь виртуальный деструктор или поведение не определено.

Это теория. Вопрос, однако, носит практический характер. Что если производный класс не добавляет членов данных?

struct Base{
   //some members
   //no virtual functions, no virtual destructor
};
struct Derived:Base{
   //no more data members
   //possibly some more nonvirtual member functions
};

int main(){
     Base* p = new Derived;
     delete p; //UB according to the quote above
}

Вопрос: существует ли какая-либо существующая реализация, для которой это было бы действительно опасно? Если да, не могли бы вы описать, как внутренние компоненты реализованы в этой реализации, что приводит к сбою / утечке кода или что-то еще? Я прошу вам верить, я клянусь , что у меня нет намерений полагаться на это поведение :)

Ответы [ 5 ]

6 голосов
/ 06 декабря 2010

Один пример, если вы предоставляете пользовательский operator new в struct Derived.Очевидно, что неправильный operator delete может привести к разрушительным результатам.

1 голос
/ 06 декабря 2010

Я не знаю ни одной реализации, в которой вышеупомянутое было бы опасно, и я думаю, что вряд ли когда-либо будет такая реализация.

И вот почему:

«неопределенное поведение» - это всеобъемлющая фраза, означающая (как все знают), что угодно может произойти. Кодекс может съесть ваш обед или вообще ничего не делать.

Однако, компилятор писатели - здравомыслящие люди, и есть разница между неопределенным поведением во время компиляции и неопределенным поведением во время выполнения. Если бы я писал компилятор для реализации, где приведенный выше фрагмент кода был опасен, было бы легко отследить и предотвратить во время компиляции. Я могу сказать, что это ошибка компиляции (или, может быть, предупреждение): Error 666: Cannot derive from class with non-virtual destructor.

Я думаю, что мне разрешено это делать, потому что поведение компилятора в этом случае не , определяемое стандартом.

0 голосов
/ 06 декабря 2010

Я полностью согласен с «Родди».

Если вы не пишете код для извращенного компилятора, предназначенного для несуществующей виртуальной машины, просто чтобы доказать, что так называемое неопределенное поведение может кусаться - нет проблем.

Смысл «острого зуба» в отношении пользовательских new/delete операторов здесь неприменим.Потому что виртуальный д'тор не решит проблему, которую он или она каким-либо образом решит.

Однако это хороший момент.Это означает, что модель, в которой вы предоставляете виртуальный d'or и тем самым включаете создание / удаление полиморфного объекта, является дефектной по своему дизайну.Более правильным решением является оснащение таких объектов виртуальной функцией, которая выполняет две вещи одновременно: вызывает ее (правильный) деструктор, а также освобождает память так, как она должна быть освобождена.Простыми словами - уничтожить объект соответствующими средствами, которые известны самому объекту.

0 голосов
/ 06 декабря 2010

В вашем конкретном случае, когда у вас нет какого-либо члена данных, объявленного в производном классе, и если у вас нет пользовательских операторов new / delete (как указано Sharptooth), у вас могут не возникнуть проблемы, но выгарантировать, что ни один пользователь никогда не выведет ваш класс?Если вы не сделаете свой деструктор Base's виртуальным, ни один из классов, производных от Derived, не сможет вызывать свои деструкторы, если объекты производных классов используются через указатель Base.

Также существует общее мнение, что если у вас есть виртуальные функции в базовом классе, деструктор должен быть сделан виртуальным.Так что лучше никого не удивлять :) 1006 *

0 голосов
/ 06 декабря 2010

Я не могу отвечать за конкретные компиляторы, вы должны спросить авторов компилятора.Даже если компилятор работает сейчас, он может не работать в следующей версии, поэтому я не буду полагаться на него.

Вам нужно такое поведение?

Позвольте мне предположить, что

  1. Вы хотите иметь возможность иметь указатель базового класса, не видя производного класса, и
  2. Не иметь v-таблицу в Base и
  3. Уметь очищать вуказатель базового класса.

Если это ваши требования, это можно сделать с помощью boost :: shared_ptr или вашей собственной адаптации.

В точке, где вы передаете указатель, вы передаетеboost :: shared_ptr с фактическим «Derived» снизу.При удалении он будет использовать деструктор, созданный при создании указателя, который использует правильное удаление.Вы, вероятно, должны предоставить Base защищенный деструктор, хотя и в целях безопасности.

Обратите внимание, что все еще существует v-таблица, но она находится в базе удаления общих указателей, а не в самом классе.

Комусоздайте свою собственную адаптацию, если вы используете boost :: function и boost :: bind, вам вообще не нужен v-стол.Вы просто получаете свой boost :: bind, чтобы обернуть базовый Derived * и вызовы функции delete на нем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...