Интересный случай удаления и деструктора (C ++) - PullRequest
2 голосов
/ 23 марта 2010

У меня есть фрагмент кода, где я могу вызвать деструктор несколько раз и получить доступ к функциям-членам, даже если деструктор был вызван с сохраненными значениями переменных-членов. Я все еще был в состоянии получить доступ к функциям-членам после того, как я вызвал delete, но переменные-члены были аннулированы (все к 0). И я не могу удвоить delete. Пожалуйста, объясните это.

#include <iostream>
using namespace std;

template <typename T>
void destroy(T* ptr)
{
    ptr->~T();
}

class Testing
{
public:
    Testing() : test(20)
    {

    }

    ~Testing()
    {
        printf("Testing is being killed!\n");
    }

    int getTest() const
    {
        return test;
    }

private:
    int test;
};

int main()
{
    Testing *t = new Testing();
    cout << "t->getTest() = " << t->getTest() << endl;

    destroy(t);
    cout << "t->getTest() = " << t->getTest() << endl;

    t->~Testing();
    cout << "t->getTest() = " << t->getTest() << endl;

    delete t;
    cout << "t->getTest() = " << t->getTest() << endl;

    destroy(t);
    cout << "t->getTest() = " << t->getTest() << endl;

    t->~Testing();
    cout << "t->getTest() = " << t->getTest() << endl;

    //delete t; // <======== Don't do it! Double free/delete!
    cout << "t->getTest() = " << t->getTest() << endl;

    return 0;
}

Ответы [ 6 ]

16 голосов
/ 23 марта 2010

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

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

Но в любом случае это ошибка программирования.

6 голосов
/ 23 марта 2010

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

Это неопределенное поведение, что означает, что все может произойти, может даже показаться, что оно делает правильные вещи.

5 голосов
/ 23 марта 2010

"Как только деструктор вызван для объект, объект больше не существует; поведение не определено, если деструктор вызывается для объекта чья жизнь закончилась "

Проект стандарта C ++ §12.4.12

Как отмечали другие, это не означает, что реализация всегда будет делать что-то явно нежелательное (например, ошибку сегментации). Это значит, что он может делать все, что угодно.

2 голосов
/ 23 марта 2010

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

2 голосов
/ 23 марта 2010

Вы вызвали неопределенное поведение, все ставки выключены.

1 голос
/ 23 марта 2010

Прямой вызов деструктора, как вы делаете в destroy() и непосредственно в main(), на самом деле не приводит к уничтожению объекта в C ++.Только оператор delete в этом коде делает это.Поскольку деструктор T доброкачественный (он просто печатает), это почти не действует.

Поскольку ни одна из функций-членов не является виртуальной, вызов их после уничтожения все равно приведет к нужному коду для выполнения.Оказавшись там, указатель this может быть недействительным (после вашего вызова на delete), но это не мешает коду разыменовать указатель и возвращать значение int значения элемента.

...