Многократное C++ удаление памяти, указанной несколькими объектами
Другой вопрос об удалении указателя в C++ приведен в следующем примере:
class Foo {
public:
int *p;
~Foo() {
delete p; p = NULL;
}
};
Foo *f1 = new Foo();
Foo *f2 = new Foo();
f1->p = new int(1);
f2->p = f1->p;
delete f2; // ok
delete f1; // no error?
Почему я не получил ошибку при вызове "delete f1"? я не удалил один и тот же адрес (*p) дважды?
Если я непосредственно удаляю указатели в последних 2 строках кода, я получу ошибку.
delete f2->p; // ok
delete f1->p; // error!! *** glibc detected *** double free or corruption (fasttop) ***
5 ответов
Это ОЧЕНЬ плохо. Однако C++ не обязательно будет делать что-либо здесь. Это "неопределенное" поведение. Это не значит, что он потерпит крах, но, скорее всего, вызовет плохое дерьмо (тм).
Изменить: Кроме того, в вашем втором примере тот факт, что он падает, является лишь частью "неопределенного" поведения. Неизвестно, какой будет реакция.
Поверь мне, ты не хочешь этого делать.
Взгляни на boost::shared_ptr
: он позволяет элегантно работать с указателями, не слишком заботясь об их удалении.
class Foo {
public:
boost::shared_ptr<int> p;
};
Foo *f1 = new Foo();
Foo *f2 = new Foo();
f1->p.reset(new int(1));
f2->p = f1->p;
delete f2; // ok
delete f1; // no error and its guaranteed !
Если вы не хотите использовать boost
некоторые компиляторы уже предоставляют std::tr1::shared_ptr
которая имеет похожую семантику.
Почему я не получил ошибку при вызове "delete f1"? я не удалил один и тот же адрес (*p) дважды?
Да, вы действительно удалили этот объект дважды.
Тем не менее, результатом этого является неопределенное поведение. Это может привести к тому, что система во время выполнения поймает ошибку и выдаст сообщение, но это также может привести к тому, что ваш HD будет отформатирован, противные носовые демоны будут гоняться за вами по офису к удовольствию ваших коллег или вас или ваша девушка забеременела. Или тогда это может также "работать", что бы это ни значило в данном случае.
Если вы хотите использовать несколько указателей на одну и ту же память, инкапсулируйте их в boost::shared_ptr
, что гарантирует, что базовая память не будет delete-d, пока последняя ссылка на нее не выйдет из области видимости.
#include <boost/shared_ptr.hpp>
class Foo {
public:
boost::shared_ptr<int> p;
~Foo() {
p.reset();
}
};
Foo *f1 = new Foo();
Foo *f2 = new Foo();
f1->p.reset(new int(1));
f2->p = f1->p;
delete f2; // ok
delete f1; // no error
Конечно, двойное удаление - неопределенное поведение. Это также может измениться, если между отладочной и релизной сборками. Другие указали вам на благость boost::shared_ptr
и тому подобное.
Тем не менее, полезно также упомянуть, как вы можете найти такие ошибки:
Запустите вашу программу в valgrind.
В Windows - это старый коммерческий аналог valgrind.