Почему я не должен использовать значение "this" после "delete this"?

В этот параграф С++ FAQ обсуждается использование конструкции delete this. В списке указаны 4 ограничения.

Ограничения с 1 по 3 выглядят вполне разумно. Но почему существует ограничение 4 на то, что я "не должен его проверять, сравнивать с другим указателем, сравнивать его с NULL, печатать его, бросать, делать с ним что-нибудь"?

Я имею в виду this - еще один указатель. Почему я не могу reinterpret_cast его на int или вызвать printf() для вывода своего значения?

Ответы

Ответ 1

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

Ответ 2

Значение 'this' после вызова delete - undefined, и поведение всего, что вы делаете с ним, также undefined. Хотя я ожидал бы, что большинство компиляторов сделают что-то разумное, нет ничего (в спецификации), чтобы остановить компилятор от решения, что его поведение в этом конкретном случае будет испускать код для форматирования вашего жесткого диска. Вызов undefined поведение (почти) всегда является ошибкой, даже когда ваш конкретный компилятор ведет себя так, как вам хотелось бы.

Вы можете обойти это, взяв копию указателя (как целого) перед вызовом delete.

Ответ 3

Ага!

3.7.3.2/4: "... функция освобождения освобождает хранилище, на которое ссылается указатель, делая недействительными все указатели, ссылающиеся на любую часть освобожденного хранилища. Эффект использования недопустимого значения указателя (включая его передачу к функции освобождения) undefined".

Обратите внимание, что это говорит "использование значения", а не "разыменование указателя".

Этот абзац не относится к this, он применяется ко всему, что было удалено.

Ответ 4

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

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

Из сообщения: "не следует проверять его, сравнивать с другим указателем, сравнивать его с NULL, печатать его, бросать, делать что-нибудь с ним"?

Все эти действия могут вызывать связанные с оператором функции, которые оцениваются с помощью указателя undefined. То же самое для кастинга.

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

Ответ 5

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

Ответ 6

b/c адрес, на который это ссылается, теперь undefined, и вы не знаете, что может быть там...

Ответ 7

В многопоточной программе, когда вы delete указатель, свободное пространство может быть выделено другим потоком, перезаписав пространство, используемое this. Даже в однопоточной программе, если вы не очень осторожны в том, что вы вызываете перед return ing, все, что вы делаете после delete this, может выделять память и перезаписывать то, на что указывалось this.

В исполняемом файле Microsoft Visual С++, скомпилированном в режиме отладки, delete ввод указателя приводит к немедленной перезаписке его памяти с помощью тестового шаблона 0xCC (неинициализированные переменные также инициализируются с помощью этого шаблона), чтобы помочь в выявлении ошибок висячего указателя такой как этот.

Это напоминает мне, когда я исправил ошибку в онлайн-игре, в которой конструктор объекта Fire удалил самый старый Fire, если общее количество Fires достигло определенного числа. Удаленный Fire иногда был родительским Fire, создающим новую Fire - bam, оборванную ошибку указателя! Только из-за удачи эта ошибка взаимодействовала с алгоритмом распределения памяти полностью предсказуемым образом (удаленный Fire всегда был перезаписан новым Fire таким же образом), в противном случае это вызвало бы деинхронизацию между онлайн-игроками. Я нашел эту ошибку при переписывании способа распределения памяти в игре. Из-за его предсказуемости, когда я ее исправил, я также смог реализовать эмуляцию своего поведения для совместимости со старыми игровыми клиентами.