Многократное 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.

Другие вопросы по тегам