Явный вызов деструктора

Я наткнулся на следующий фрагмент кода:

#include <iostream>
#include <string>
using namespace std;
class First
{
string *s;
public:
First() { s = new string("Text");}
~First() { delete s;}
void Print(){ cout<<*s;}
};

int main()
{
First FirstObject;
FirstObject.Print();
FirstObject.~First();
}

В тексте сказано, что этот фрагмент должен вызвать ошибку во время выполнения. Теперь я не был уверен в этом, поэтому я попытался скомпилировать и запустить его. Это сработало. Странная вещь, несмотря на простоту используемых данных, программа заикалась после печати «Текст» и только через одну секунду она завершила.

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

Простой поиск подтвердил, что явный вызов деструктора для автоматизированного объекта опасен, поскольку второй вызов (когда объект выходит из области видимости) имеет неопределенное поведение. Так что мне повезло с моим компилятором (VS 2017) или этой конкретной программой.

Является ли текст просто неправильным об ошибке во время выполнения? Или это действительно распространенная ошибка? Или, может быть, мой компилятор реализовал какой-то механизм защиты от подобных вещей?

21

Решение

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

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

Так что мне повезло с моим компилятором (VS 2017) или этой конкретной программой.

Я бы сказал, что ты несчастливый. Лучшее (для вас, кодера), которое может случиться с UB, — это сбой при первом запуске. Если кажется, что он работает нормально, падение может произойти 19 января 2038 года в производстве.

Является ли текст просто неправильным об ошибке во время выполнения? Или это действительно распространенная ошибка? Или, может быть, мой компилятор реализовал какой-то механизм защиты от подобных вещей?

Да, текст как-то не так. Неопределенное поведение не определено. Ошибка во время выполнения — только одна из многих возможностей (включая носовые демоны).

Хорошее чтение о неопределенном поведении: Что такое неопределенное поведение?

34

Другие решения

Нет, это просто неопределенное поведение из черновика стандарта C ++ [Class.dtor] р16:

Как только деструктор вызывается для объекта, объект больше не существует; поведение не определено, если деструктор вызывается для объекта, срок жизни которого истек ([basic.life]).
[Пример: если деструктор для автоматического объекта вызывается в явном виде, и блок впоследствии остается таким образом, который обычно вызывает неявное уничтожение объекта, поведение не определено.
— конец примера

и мы можем видеть из определение неопределенного поведения:

поведение, к которому этот документ не предъявляет никаких требований

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

Дополнительно [Class.dtor] р15 дает больше контекста в нормативном разделе, который я цитирую выше:

[Примечание: явные вызовы деструкторов нужны редко.
Одно из применений таких вызовов — для объектов, размещенных по определенным адресам с использованием нового выражения размещения.
Такое использование явного размещения и уничтожения объектов может быть необходимо для управления выделенными аппаратными ресурсами и для записи средств управления памятью.
Например,

void* operator new(std::size_t, void* p) { return p; }
struct X {
X(int);
~X();
};
void f(X* p);

void g() {                      // rare, specialized use:
char* buf = new char[sizeof(X)];
X* p = new(buf) X(222);       // use buf[] and initialize
f(p);
p->X::~X();                   // cleanup
}

— конец примечания]

15

Является ли текст просто неправильным об ошибке во время выполнения?

Это неверно.

Или это действительно распространенная ошибка? Или, может быть, мой компилятор реализовал какой-то механизм защиты от подобных вещей?

Вы не можете знать, и это то, что происходит, когда ваш код вызывает Неопределенное поведение; Вы не знаете, что произойдет, когда вы выполните это.

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


* Потому что, если вы получили ошибку, вы бы начали отладку, в противном случае, например, в большом проекте вы могли бы ее пропустить …

9
По вопросам рекламы [email protected]