Почему мой указатель не удаляет мой указатель?
Итак, чтобы лучше понять новый/удалить (действительно, чтобы доказать себе небольшими примерами, почему виртуальные деструкторы необходимы для интерфейсов), я хочу понять утечки памяти, чтобы я мог бояться их. Но мне нелегко пропустить мою утечку, так сказать; на самом деле, мне тоже сложно с новым/удалить тоже.
Вот моя самая простая версия:
int* P1 = new int(43);
cout<<"P1 = "<<P1<<endl;
cout<<"*P1 = "<<*P1<<endl;
delete P1;
cout<<"P1 = "<<P1<<endl;
cout<<"*P1 = "<<*P1<<endl;
Отпечатки:
P1 = 0xcc0340
*P1 = 43
P1 = 0xcc0340
*P1 = 43
У меня было что-то более сложное внутри класса, но этот пример иллюстрирует мой отказ. Я думал, что delete берет указатель и освобождает его память, тем самым аннулируя указатель или, по крайней мере, то, на что он указывает? Я должен делать что-то очень простое, очень неправильное.
Ответы
Ответ 1
Вы вызываете поведение undefined. Это означает, что все может случиться. Поскольку что-то действительно произошло, все ведет себя как задокументировано. (Иногда "что-то" выглядит очень похоже на что-то еще, что вы можете ошибочно ожидать. Выполнение именно того, что вы пытаетесь достичь, - один из возможных разрешенных экземпляров поведения "undefined".)
Обратите также внимание на то, что "утечка памяти" представляет собой нечто противоположное тому, что вы пытаетесь сделать - в утечке памяти вы забываете освободить память, тогда как вы уже освободили память и теперь обращаетесь к недопустимой памяти.
Здесь настоящая утечка памяти, которая также не вызывает поведения undefined - не путайте "плохо, но правильно" с "неправильным" программированием!
int * factorial(int * n)
{
if (*n == 0) return new int(1);
else return new int(*n * *factorial(*n - 1));
}
Ответ 2
Я должен делать что-то очень простое очень плохо.
Ты абсолютно прав. Ваш код делает что-то очень простое очень неправильно, и вы заплатили конечную цену - не видя никаких отрицательных результатов. Иногда вы делаете что-то неправильно, и ничего плохого не происходит. Это худший возможный результат, потому что вы можете не понимать, что ваш код неправильный, и вы продолжите его использовать, пока однажды ваш код не сломается неожиданно, и вы не будете знать, где и как, потому что он всегда работал раньше. Это сломается где-то совершенно не относящееся к фактической части, что неправильно, и вы будете тратить часы, пытаясь отследить ее и выяснить, почему она сломана.
Когда вы делаете что-то неправильно, это поведение undefined. Это означает, что все может случиться. В лучшем случае ваш код разбивается, и в нем появляются сбои и мигающие огни, говоря: "ВЫ ИСПОЛЬЗУЕТЕ ЗАПОЛНЕННУЮ ПАМЯТЬ", и все очень ясно. В худшем случае, демоны вылетают из вашего носа. Но чуть выше этого, второй худший возможный результат заключается в том, что все работает, как вы намеревались.
Ответ 3
Это будет утечка памяти:
int* P1 = new int(43);
P1 = new int(42);
Выделение памяти без повторного удаления.
Ответ 4
// Reserve some memory for an int and set that memory to the value 43.
int* P1 = new int(43);
// Print the address of the reserved memory.
cout<<"P1 = "<<P1<<endl;
// Print the contents of that memory.
cout<<"*P1 = "<<*P1<<endl;
// Free the memory - it is no longer reserved to you.
delete P1;
// int* P2 = new int(47);
// Print the address of the memory. It still holds the address to
// the memory that used to be reserved for you.
cout<<"P1 = "<<P1<<endl;
// Print the current value of the memory that used to be reserved.
cout<<"*P1 = "<<*P1<<endl;
Если вы раскомментируете строку P2, вполне вероятно, что ей будет присвоена та же память, которая изменит значение, напечатанное на последней строке.
Доступ к памяти, освобожденной с помощью delete
, вызывает поведение undefined, как указывали другие. undefined включает в себя случайные сбои в некоторых случаях (только во время полной луны, возможно?;-). Он также включает в себя все, что отлично работает на данный момент, но с ошибкой, являющейся миной, которая может отключиться, когда вы делаете другое изменение где-либо еще в своей программе.
Акустик памяти - это когда вы выделяете память с помощью new
и никогда не освобождаете ее с помощью delete
. Обычно этого не замечают, пока кто-то не запустит вашу программу на более длительный период и не узнает, что она ест всю память системы.
Ответ 5
Этот код
delete P1;
cout<<"P1 = "<<P1<<endl;
cout<<"*P1 = "<<*P1<<endl;
вызывает undefined -поведение. Так что все может случиться. Это намного легче вызвать утечку памяти:
for(;;) new int;
Ответ 6
Выделение удалённого указателя - это undefined поведение, как уже объяснялось, т.е. вы делаете что-то неправильно. Компиляторы могут помочь вам найти эту ошибку, изменив значение указателя на какое-то магическое значение, которое всегда приведет к разыменованию указателя, и может дать вам понять, что он был ранее удален.
Быстрый тест показал для меня следующее поведение:
MSVC2010:
debug build: * P1 = 0xfeeefeee
релиз: * P1 = <random>
GCC 4.6:
debug и release: * P1 = 0
Итак, вам просто (не повезло), что вы снова получили первоначальное значение. Я рекомендую вам всегда строить режим отладки для тестирования ваших программ (даже если вы не отлаживаете), потому что он добавляет дополнительные проверки работоспособности в ваш код.
Ответ 7
Память освобождается, но не очищается. Значение может оставаться в памяти, пока какой-либо другой процесс не напишет новое значение в этом месте.
Ответ 8
Удалить не приводит к недействительности указателя. Он освобождает память, которую он указывает обратно в кучу. В этом случае память не была повторно использована и, следовательно, все еще содержит один и тот же контент. В какой-то момент память будет повторно использоваться, а затем вы получите поведение undefined, о котором говорят другие.
Однако это не утечка памяти. Это когда вы указываете указатель на другое распределение памяти (или просто отбрасываете указатель), не освобождая память, на которую указывает. Первый затем остается выделенным и, поскольку у вас нет ссылок на него, вы не можете его освободить.