Почему именно второй раз вызывает деструктор неопределенное поведение в C ++? - PullRequest
16 голосов
/ 05 мая 2010

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

Например:

class Class {
public:
    ~Class() {}
};
// somewhere in code:
{
    Class* object = new Class();
    object->~Class();
    delete object; // UB because at this point the destructor call is attempted again
}

В этом примере класс сконструирован таким образом, что деструктор может вызываться несколько раз - никаких вещей, таких как двойное удаление, быть не может. Память все еще выделяется в точке, где вызывается delete - первый вызов деструктора не вызывает ::operator delete() для освобождения памяти.

Например, в Visual C ++ 9 приведенный выше код выглядит работающим. Даже определение UB в C ++ не запрещает работать вещам, квалифицированным как UB. Поэтому для того, чтобы код выше нарушил некоторые особенности реализации и / или платформы, требуются.

Почему именно вышеприведенный код нарушается и при каких условиях?

Ответы [ 16 ]

0 голосов
/ 05 мая 2010

Причина в том, что в отсутствие этого правила ваши программы стали бы менее строгими. Быть более строгим - даже если это не применяется во время компиляции - хорошо, потому что, в свою очередь, вы получаете больше предсказуемости того, как программа будет вести себя. Это особенно важно, когда исходный код классов не находится под вашим контролем.

Множество понятий: RAII, умные указатели и просто общее выделение / освобождение памяти полагаются на на это правило. Количество раз, которое деструктор будет вызывать (один), составляет существенный для них. Поэтому документация для таких вещей обычно обещает: « Используйте наши классы в соответствии с правилами языка C ++, и они будут работать правильно! »

Если бы такого правила не было, оно указывало бы как " Используйте наши классы в соответствии с правилами языка C ++, и да, не вызывайте его деструктор дважды, тогда они будут работать правильно. " Многие спецификации звучат именно так. Эта концепция слишком важна для языка, чтобы пропустить ее в стандартном документе.

Это причина. Ничего, связанного с бинарными внутренними объектами (которые описаны в ответе Potatoswatter ).

0 голосов
/ 05 мая 2010

Это неопределенное поведение, потому что стандарт прояснил, для чего используется деструктор, и не решил, что должно произойти, если вы используете его неправильно. Неопределенное поведение не обязательно означает «аварийный удар», это просто означает, что стандарт не определил его, поэтому он остается на усмотрение реализации.

Хотя я не слишком бегло говорю на C ++, моя интуиция говорит мне, что реализация может либо рассматривать деструктор как просто другую функцию-член, либо фактически уничтожать объект при вызове деструктора. Так что это может сломаться в некоторых реализациях, но, возможно, не будет в других. Кто знает, это не определено (обратите внимание на демонов, вылетающих из носа, если вы попытаетесь).

0 голосов
/ 05 мая 2010

Причина в том, что ваш класс может быть, например, умным указателем с подсчетом ссылок. Таким образом, деструктор уменьшает счетчик ссылок. Как только этот счетчик достигнет 0, фактический объект должен быть очищен.

Но если вы дважды вызовете деструктор, то счет будет сбит с толку.

Та же идея и для других ситуаций. Возможно, деструктор записывает 0 в часть памяти, а затем освобождает ее (чтобы вы случайно не оставили пароль пользователя в памяти). Если вы попытаетесь снова записать в эту память - после ее освобождения - вы получите нарушение прав доступа.

Просто имеет смысл, чтобы объекты строились один раз и разрушались один раз.

0 голосов
/ 05 мая 2010

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

0 голосов
/ 05 мая 2010

Полагаю, это было классифицировано как неопределенное, потому что большинство двойных удалений опасны, и комитет по стандартам не хотел добавлять исключение в стандарт для относительно небольшого числа случаев, когда они не должны быть.

Что касается того, где ваш код может сломаться; вы можете найти разрывы кода в отладочных сборках на некоторых компиляторах; многие компиляторы рассматривают UB как «делают то, что не повлияет на производительность для четко определенного поведения» в режиме выпуска и «вставляют проверки для обнаружения плохого поведения» в отладочных сборках.

0 голосов
/ 05 мая 2010

По определению, деструктор "уничтожает" объект и уничтожает объект дважды, бессмысленно.

Ваш пример работает, но его сложно, как правило, работает

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