Подтвердить что ты не робот

Почему ничего не уничтожает?

Я немного играю с динамическим распределением памяти, но я не понимаю. При распределении некоторой памяти с помощью оператора 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 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;  
}  

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

4b9b3361

Ответ 1

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

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

В тот момент, когда вы выполняете свой последний *(pTest), вы получаете поведение undefined. Это связано с тем, что pTest не указывает на действительный объект, а разыменование такого указателя - undefined. То, что вы видите, полностью разрешено: undefined output.

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

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

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

#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.
}

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

Ответ 2

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

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

delete pTest;
pTest = 0;

Ответ 3

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

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

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

Ответ 4

Оператор

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

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

Ответ 5

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

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

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

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

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

Ответ 6

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

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

Ответ 7

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

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

delete pTest;

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

cout << *pTest << endl;

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

Ответ 8

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

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