Почему не удаляет уничтожить что-нибудь? - PullRequest
21 голосов
/ 19 июля 2010

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

Но когда я пытаюсь, эта delete команда, похоже, не работает, так как пространство, на которое указывает указатель, похоже, не освобождено.

Давайте возьмем этот действительно базовый кусок кода в качестве примера:

#include <iostream>  

using namespace std;

int main()  
{  
    //I create a pointer-to-integer pTest, make it point to some new space,  
    // and fulfill this free space with a number;  
    int* pTest;  
    pTest = new int;  
    *(pTest) = 3;  
    cout << *(pTest) << endl; 

    // things are working well so far. Let's destroy this
    // dynamically allocated space!
    delete pTest;

    //OK, now I guess the data pTest pointed to has been destroyed 
    cout << *(pTest) << endl; // Oh... Well, I was mistaking.  

    return 0;  
}  

Любая подсказка?

Ответы [ 8 ]

55 голосов
/ 19 июля 2010

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

В 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 ++.

10 голосов
/ 19 июля 2010

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

Рекомендуется установить указатель на 0 после вызова delete:

delete pTest;
pTest = 0;
5 голосов
/ 19 июля 2010

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

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

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

5 голосов
/ 19 июля 2010
Оператор удаления

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

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

2 голосов
/ 19 июля 2010

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

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

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

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

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

1 голос
/ 19 июля 2010

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

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

delete pTest;

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

cout << *pTest << endl;

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

1 голос
/ 19 июля 2010

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

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

1 голос
/ 19 июля 2010

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

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

...