должен ли деструктор вызываться с размещением нового даже на том же типе - PullRequest
3 голосов
/ 18 марта 2019
#include <new>

struct X
{
    ~X() { std::cout << "destroyed" << std::endl; }
    int x;
};

int main(int argc, const char * const * const argv)
{
    X x{1};

    new (&x) X{2};

    std::cout << x.x << std::endl;

    return 0;
}

выход

2
destroyed

Что я знаю, так это то, что деструктор должен вызываться всегда, когда используется новое размещение. Однако в этом примере кода деструктор неявно вызывается в конце основного кода, поэтому его повторный вызов - это неопределенное поведение, я полагаю. так что теперь я хочу знать, должен ли деструктор всегда вызываться при использовании размещения new или есть определенные условия, при которых деструктор не должен вызываться?

Ответы [ 2 ]

2 голосов
/ 18 марта 2019

Это явно указано в стандарте C ++

[basic.life]

5 Программа может завершить время жизни любого объекта, повторно используя хранилище, которое занимает объект или путем явного вызова деструктор для объекта типа класса с нетривиальным деструктор. Для объекта типа класса с нетривиальным деструктор, программа не обязана вызывать деструктор явным образом до повторного использования хранилища, которое занимает объект, или вышел; однако, если нет явного вызова деструктора или если выражение delete не используется для освобождения хранилища, деструктор не должен вызываться неявно и любая программа, которая зависит на побочные эффекты, вызванные деструктором, имеет неопределенное поведение.

Последнее предложение оставляет немного места для маневра относительно возможности неопределенного поведения 1 . Но, в конечном счете, единственные типы, для которых это четко определено, это те, для которых деструктор действительно тривиален.


1 - Во всяком случае, на момент написания этого.

2 голосов
/ 18 марта 2019

Что я знаю, так это то, что деструктор всегда должен вызываться при использовании нового размещения.

Да, за исключением случаев, когда тип тривиально разрушаем.

В этом случае вы должны уничтожить ранее построенный объект перед размещением нового:

X x{1};
x.~X();
try {
    new (&x) X{2};
} catch(...) {
    std::abort(); // no way to recover
}

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

Безопаснее повторно использовать память тривиального объекта:

alignas(alignof(X)) std::byte arr[sizeof(X)];
new (arr) X{2};
x.~X();
...