Почему я должен вызывать delete на «оригинальном» указателе? - PullRequest
3 голосов
/ 10 января 2012

Я хотел бы знать, почему в следующем коде первое удаление не освобождает память:

#include <list>
#include <stdio.h>

struct abc {
    long a;

    abc() {
        puts("const");
    }
    ~abc() {
        puts("desc");
    }
};

int main() {

    std::list<abc*> test;

    abc* pA = new abc;
    printf("pA: 0x%lX\n", (unsigned long int)pA);
    test.push_back(pA);

    abc* pB = test.back();

    printf("pB: 0x%lX\n", (unsigned long int)pB);
    delete pB; // just ~abc()

    test.pop_back();

    delete pA; // ~abc() and free (works)

    puts("before double-free");

    delete pA; // ~abc() and second free (crash)

    return 0;
}

Вывод:

const
pA: 0x93D8008
pB: 0x93D8008
desc
desc
before double-free
desc
*** glibc detected *** ./test: double free or corruption (fasttop): 0x093d8008 ***
...

Я пробовал с free() также, но с тем же поведением.

Ответы [ 4 ]

4 голосов
/ 10 января 2012
delete pA; // ~abc() and free (works)

puts("before double-free");

delete pA; // ~abc() and second free (crash)

Эти delete операторы не необходимы, когда вы пишете delete pB.У вас неправильное представление, что delete pB вызывает только деструктор.Нет, он вызывает деструктор и также освобождает память.

Кроме того, поскольку вы уже написали delete pB, следующие два дополнительных выражения delete вызывают неопределенное поведение , что означаетможет произойти все что угодно: программа может или может не аварийно завершить работу!

Просмотрите следующие темы:

2 голосов
/ 10 января 2012

Ваше первое удаление переводит указатель в неверное состояние. Поэтому использование этого указателя теперь ведет к неопределенному поведению.

int* p = new int;
delete p;
delete p; //undefined behaviour

Стандарт гарантированно будет в порядке (как указано в первом комментарии):

int* p = new int;
delete p;
p = 0;
delete p; //fine
1 голос
/ 10 января 2012

Это просто особенность вашего компилятора / платформы, которую вы должны были вызвать delete дважды на pA до того, как glibc пожаловался ... например, на моей текущей платформе, которая является OSX 10.6.8 с gcc версия i686-apple-darwin10-gcc-4.2.1, я получаю следующий вывод:

const
pA: 0x100100080
pB: 0x100100080
desc
desc
test(14410) malloc: *** error for object 0x100100080: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap

Таким образом, вы можете увидеть первый вызов delete на pA, что по стандартным результатамв неопределенном поведении, поскольку вы не установили указатель на NULL или 0, он был пойман как попытка освободить уже освобожденную память во время выполнения C ++ на моей платформе.

Поскольку результатыдвойное удаление - это «неопределенное поведение», это зависит от реализации и платформы в отношении того, что происходит.

Вы можете быстрее обнаруживать ошибки памяти выделения / освобождения, если используете ссылку на память glibc.проверка согласованности путем компиляции с флагом -lmcheck.

1 голос
/ 10 января 2012

Во-первых, вы не можете free памяти, выделенной с помощью new, вы должны использовать delete.

Во-вторых, только потому, что glibc не сразу обнаруживает двойное освобождение, как вы знаете, delete pB; не освобождает это?Это то, что делает delete, и из своей собственной регистрации вы каждый раз передаете один и тот же адрес delete.

В-третьих: что вы на самом деле пытаетесь достичь?

...