Явный вызов деструктора - PullRequest
0 голосов
/ 12 ноября 2018

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

#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) или этой конкретной программой.

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

Ответы [ 3 ]

0 голосов
/ 12 ноября 2018

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

Это неправильно.

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

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

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


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

0 голосов
/ 12 ноября 2018

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

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

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

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

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

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

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

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
}

- конечная нота]

0 голосов
/ 12 ноября 2018

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

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

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

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

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

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

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

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