Разрешено ли оптимизатору компилятора C ++ нарушать мою способность вызывать деструктор несколько раз? - PullRequest
4 голосов
/ 04 мая 2010

Однажды у нас было интервью с очень опытным разработчиком C ++, который не смог ответить на следующий вопрос: необходимо ли вызывать деструктор базового класса из деструктора производного класса в C ++?

Очевидно, что ответ нет, C ++ в любом случае автоматически вызовет деструктор базового класса . Но что, если мы попытаемся сделать звонок? На мой взгляд, результат будет зависеть от того, можно ли вызвать деструктор базового класса дважды, не вызывая ошибочного поведения.

Например, в этом случае:

class BaseSafe {
public:
    ~BaseSafe()
    {
    }
private:
    int data;
};

class DerivedSafe {
public:
    ~DerivedSafe()
    {
        BaseSafe::~BaseSafe();
    }
};

все будет хорошо - деструктор BaseSafe можно вызвать дважды безопасно и программа будет работать нормально.

Но в этом случае:

class BaseUnsafe {
public:
    BaseUnsafe()
    {
       buffer = new char[100];
    }
    ~BaseUnsafe ()
    {
        delete[] buffer;
    }
private:
    char* buffer;
};

class DerivedUnsafe {
public:
    ~DerivedUnsafe ()
    {
        BaseUnsafe::~BaseUnsafe();
    }
};

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

Похоже, что легко избежать UB во втором случае. Просто установите buffer в нулевой указатель после delete[].

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

Разрешено ли это делать компилятору?

Ответы [ 2 ]

13 голосов
/ 04 мая 2010

Стандарт 12,4 / 14

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

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

Но даже если установка буфера в null не была удалена компилятором, похоже, что двойной вызов деструктора приведет к UB.

4 голосов
/ 04 мая 2010

Вызов деструктора преобразует объект в необработанную память. Вы не можете разрушить сырую память; это неопределенное поведение. Компилятор C ++ имеет право делать все, что захочет. Хотя маловероятно, что он превратит ваш компьютер в творог, он может сознательно вызвать SEGFAULT «пощечину» (по крайней мере, в режиме отладки).

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