Почему удаление моего указателя не удаляет мой указатель? - PullRequest
15 голосов
/ 06 августа 2011

Чтобы лучше понять новое / удалить (на самом деле, чтобы доказать себе на небольших примерах, зачем нужны виртуальные деструкторы для интерфейсов), я хочу понять утечки памяти, чтобы жить в страхе перед ними.Но мне трудно, так сказать, протекать;на самом деле, мне тоже тяжело с новой / удалить.

Вот моя самая простая версия:

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

У меня было что-то ещесложный внутри класса, но этот пример иллюстрирует мою неудачу.Я думал, что удаление берет указатель и освобождает его память, тем самым лишает законной силы указатель или, по крайней мере, на что он указывает?Должно быть, я делаю что-то очень простое, очень неправильное.

Ответы [ 8 ]

25 голосов
/ 06 августа 2011

Вы вызываете неопределенное поведение .Это означает, что все может случиться.Поскольку что-то действительно произошло, все ведет себя как документально.(Иногда «что-то» выглядит очень похоже на что-то другое, что вы можете ошибочно ожидать. Выполнение именно того, что, как вы думаете, вы пытаетесь достичь, является одним из возможных допустимых случаев «неопределенного поведения».)

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

Вотреальная утечка памяти, которая также не вызывает неопределенное поведение - не путайте «плохое, но правильное» с «неправильным» программированием!

int * factorial(int * n)
{
  if (*n == 0) return new int(1);
  else return new int(*n * *factorial(*n - 1));
}
23 голосов
/ 06 августа 2011

Я должен делать что-то очень простое, очень неправильное.

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

Когда вы делаете что-то неправильно, это неопределенное поведение. Это означает, что все может случиться. В лучшем случае ваш код вылетает, а ошибки и мигающие огни загораются, говоря «ВЫ ИСПОЛЬЗУЕТЕ СВОБОДНУЮ ПАМЯТЬ», и все предельно ясно. В худшем случае демоны вылетают из твоего носа . Но чуть выше этого, второй худший из возможных результатов - все работает так, как вы и предполагали.

5 голосов
/ 06 августа 2011

Это будет утечка памяти:

int* P1 = new int(43);
     P1 = new int(42);

Выделение памяти без ее повторного удаления.

4 голосов
/ 06 августа 2011
// 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 включает в себя странные сбои в некоторых случаях (возможно, только в полнолуние? ;-).Он также включает в себя все, что работает на данный момент на отлично, но с ошибкой, которая может быть моей, которая может сработать всякий раз, когда вы делаете другое изменение где-либо еще в вашей программе.

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

3 голосов
/ 06 августа 2011

Этот код

delete P1;

cout<<"P1 = "<<P1<<endl;
cout<<"*P1 = "<<*P1<<endl;

вызывает неопределенное поведение.Так что все может случиться.Гораздо проще вызвать утечку памяти:

for(;;) new int;
2 голосов
/ 06 августа 2011

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

Быстрый тест показал для меня следующее поведение:MSVC2010:отладочная сборка: * P1 = 0xfeeefeeeВыпуск сборки: * P1 = GCC 4.6:сборки отладки и выпуска: * P1 = 0

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

1 голос
/ 06 августа 2011

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

0 голосов
/ 06 августа 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...