В примерах для размещения новых часто используются беззнаковые массивы в качестве основного хранилища. Шаги могут быть:
- создать массив без знака с новым
- создать объект в этом хранилище с размещением нового
- использовать объект
- уничтожить объект
- вызовите delte для массива unsigned char, чтобы освободить массив
Точка 5., кажется, работает, только если мы используем тип для основного хранилища с тривиальным деструктором. В противном случае мы бы вызвали деструктор базового типа хранилища, но без существующего объекта. Технически, мы уничтожаем группу неподписанных символов, которых нет, и нам повезло, что деструктор типа unsigned char тривиален и поэтому не работает.
А как насчет следующего кода:
struct A{ /* some members... */ };
struct B{ /* some members... B shall be same size as A */ };
int main()
{
auto ptr_to_a = new A; // A object lives @ ptr_to_a
ptr_to_a->~A(); // A object destroyed. no object living @ ptr_to_a, but storage is preserved
new (ptr_to_a) B; // B object living @ ptr_to_a.
std::launder(reinterpret_cast<b*>(ptr_to_a))->/*...*/; // use B. for this purpose we need std::launder in C++17 or we would store the pointer returned by the placement new and use it without std::launder
std::launder(reinterpret_cast<b*>(ptr_to_a))->~B(); // B object destroyed. no object living @ ptr_to_a, but storage is preserved
// at this point there is no object living @ ptr_to_a, but we need to hand back the occupied storage.
// a)
delete ptr_to_a; // undefined behavior because no object is sitting @ ptr_to_a
// b)
new (ptr_to_a) A; // create an object again to make behavior defined. but this seems odd.
delete ptr_to_a;
// c)
// some method to just free the memory somehow without invoking destructors?
return 0;
}
На https://en.cppreference.com/w/cpp/language/lifetime написано:
Как особый случай, объекты могут быть созданы в массивах без знака char или std :: byte (в этом случае говорят, что массив предоставляет хранилище для объекта), если ....
Означает ли это, что разрешено использовать только размещение новых в неподписанных массивах char и byte и поскольку у них есть тривиальный деструктор, мой пример кода устарел?
В противном случае, как насчет моего примера кода? Является ли вариант б) единственным действительным решением?
Редактировать: второй экзамен:
struct A{ /* some members... */ };
struct alignas(alignof(A)) B{ /* some members... */ };
int main()
{
static_assert(sizeof(A) == sizeof(B));
A a;
a.~A();
auto b_ptr = new (&a) B;
b_ptr->~B();
return 0;
// undefined behavior because a's destructor gets called but no A object is "alive" (assuming non trivial destructor)
// to make it work, we need to placement new a new A into a?
}