Почему не удаляет уничтожить что-нибудь?

21
задан Brian Tompsett - 汤莱恩 8 July 2015 в 21:50
поделиться

8 ответов

Пришло время узнать, что такое неопределенное поведение. :)

В C++, когда вы делаете что-то незаконное/нелепое/плохое/и т.д., стандарт часто говорит, что "это приводит к неопределенному поведению". Это означает, что с этого момента состояние вашей программы полностью негарантировано, и может произойти что угодно.

В точке, где вы выполняете последний *(pTest), вы получаете неопределенное поведение. Это происходит потому, что pTest не указывает на правильный объект, а разыменование такого указателя не определено. Поэтому то, что вы видите, вполне допустимо: неопределенный вывод.

Все, что вы сделали, это сказали: "Я закончил с этим распределением". Как только вы это сказали, вы больше не должны (и, более того, не можете) проверять или заботиться об этой памяти. Даже не имеет концептуального смысла деаллокация чего-либо, а затем попытка его использования; вы уже сказали, что закончили!

Впрочем, ваш результат в некоторой степени предсказуем: скорее всего, ваша ОС просто скажет "хорошо, спасибо за память" и все. У нее нет причин действительно "сбрасывать" память или делать что-то особенное. Это было бы действительно пустой тратой времени, когда никто (включая вашу собственную программу) не использует ее.

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

#include <iostream>

struct foo
{
    ~foo()
    {
        std::cout << "foo is gone :(" << std::endl;
    }
};

int main(void)
{
    foo* f = new foo();
    delete f; // you'll see that the object is destroyed.
}

Хотя, похоже, вы хотели посмотреть, что происходит с самой памятью. Помните, что не имеет смысла избавляться от памяти, а затем пытаться ее использовать, поэтому ответ: кто знает. Это зависит от вашей конкретной платформы, о которой C++ не заботится.

55
ответ дан 29 November 2019 в 06:16
поделиться

Оператор удаления вызывает деструктор объекта и освобождает память, ранее выделенную объекту. Это не влияет на переменную-указатель, указывающую на удаленный объект.

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

5
ответ дан 29 November 2019 в 06:16
поделиться

Что означает уничтожение данных? Я полагаю, это могло бы обнулить его, но зачем беспокоиться? Считается, что он грязный, когда мы получаем его из окружающей среды, так зачем же его чистить, прежде чем вернуть? Нам все равно, что там написано, потому что мы отказываемся от права читать это. А что касается того, почему удаление не обнуляет сам указатель:

http://www2.research.att.com/~bs/bs_faq2.html#delete-zero

1
ответ дан 29 November 2019 в 06:16
поделиться

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

Если мы добавим две дополнительные строки кода перед печатью:

delete pTest;

int *foo = new int;
*foo = 42;

cout << *pTest << endl;

Напечатанное значение pTest вполне может быть 3, как это было в вашем случае. Однако напечатанное значение также может быть 42. Поскольку указатель pTest был удален, его память была освобождена. Из-за этого возможно, что указатель foo будет указывают на то же место в памяти, на которое pTest указывал до того, как он был удалено.

1
ответ дан 29 November 2019 в 06:16
поделиться

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

Если delete действительно очистил все При удалении памяти программы будут работать значительно дольше, потому что они будут тратить много времени на очистку памяти, которая, вероятно, будет перезаписана рано или поздно в любом случае. Это может быть полезно для отладки, но в производственной среде просто не так много необходимости очищать содержимое памяти. (Крипто-ключи, конечно, хорошее исключение; очистить их перед вызовом delete или free - хорошая идея.)

1
ответ дан 29 November 2019 в 06:16
поделиться

Ответ - производительность .

Это отличное средство отладки, позволяющее заполнить всю освобожденную память недопустимым значением ( 0xCCCCCCCC , 0xDEADDEAD и т. Д.), Чтобы отловить попытки использовать устаревшие указатели на уже освобожденную память.

Но изменение освобожденной памяти требует затрат времени ЦП, поэтому по соображениям производительности ОС просто добавит освобожденный блок памяти в свой «свободный» список и оставит содержимое нетронутым.

5
ответ дан 29 November 2019 в 06:16
поделиться

Вызов delete пометит область памяти как свободную. Это не приведет к восстановлению ее старого значения.

Советую установить указатель в 0 после вызова delete:

delete pTest;
pTest = 0;
10
ответ дан 29 November 2019 в 06:16
поделиться

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

Много раз это будет просто работать, потому что память, предоставляемая new, обычно является частью большего куска выделенной памяти, которой управляет распределитель. Когда вы вызываете delete, он вызывает соответствующие деструкторы и помечает память как свободную, что обычно означает «готов к повторному использованию». Таким образом, заглянув в эту память, вы найдете те же данные, которые были там до вызова delete, или некоторые другие данные, если этот кусок памяти был переназначен после вызова new.

Обратите внимание, что ничто не запрещает, чтобы распределитель new/delete работал как тонкая оболочка вокруг функций виртуальной памяти ОС, поэтому, когда все выделенные блоки относительно страницы были освобождены, вся страница освобождается, и любая попытка доступа к ней приводит к нарушению адреса.

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

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

2
ответ дан 29 November 2019 в 06:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: