Порядок уничтожения в C ++: вызов деструктора поля перед деструктором класса

Есть ли способ вызвать деструктор поля перед деструктором класса?

Предположим, у меня есть 2 класса Small а также Big, а также Big содержит экземпляр Small как его поле как таковое:

class Small
{
public:
~Small() {std::cout << "Small destructor" << std::endl;}
};

class Big
{
public:
~Big() {std::cout << "Big destructor" << std::endl;}

private:
Small small;
};

int main()
{
Big big;
}

Это, конечно, вызывает большой деструктор перед маленьким деструктором:

Big destructor
Small destructor

мне нужно Small деструктор должен быть вызван до Big деструктор, так как он делает некоторую очистку, необходимую для Big деструктор.

Я мог бы:

  1. позвонить small.~Small() деструктор явно. -> Это, однако, вызывает Small деструктор дважды: один раз явно, и один раз после Big деструктор был казнен.
  2. иметь Small* как поле и вызов delete small; в Big деструктор

Я знаю, что я могу иметь функцию в Small класс, который делает уборку и вызывает его в Big деструктор, но мне было интересно, есть ли способ изменить порядок деструктора.

Есть ли лучший способ сделать это?

3

Решение

вызовите деструктор small. ~ Small () явно. -> Это, однако, вызывает маленький деструктор дважды: один раз явно, и один раз после того, как большой деструктор был выполнен.

Ну, я не знаю, почему вы хотите продолжать этот дряблый дизайн, но вы можете решить проблема описано в вашей первой пули, используя размещение нового.
Следует минимальный рабочий пример:

#include <iostream>

struct Small {
~Small() {std::cout << "Small destructor" << std::endl;}
};

struct Big {
Big() { ::new (storage) Small; }

~Big() {
reinterpret_cast<Small *>(storage)->~Small();
std::cout << "Big destructor" << std::endl;
}

Small & small() {
return *reinterpret_cast<Small *>(storage);
}

private:
unsigned char storage[sizeof(Small)];
};

int main() {
Big big;
}

У вас больше нет переменной типа Small, но с чем-то вроде small Функция-член в примере вы можете легко обойти это.

Идея состоит в том, что вы резервируете достаточно места для постройки Small и тогда вы можете явно вызывать его деструктор, как и раньше. Он не будет вызываться дважды, несмотря на все Big класс должен выпустить массив unsigned chars.
Кроме того, вы не будете хранить свои Small непосредственно в динамическое хранилище, так как на самом деле вы используете элемент данных вашего Big создать его в.


При этом я бы посоветовал вам разместить его в динамическом хранилище, если у вас нет веских причин поступить иначе. Использовать std::unique_ptr и сбросить его в начале деструктора Big, Ваш Small уйдет до того, как тело деструктора будет действительно выполнено, как и ожидалось, и в этом случае деструктор не будет вызван дважды.


РЕДАКТИРОВАТЬ

Как предлагается в комментариях, std::optional может быть другим жизнеспособным решением вместо std::unique_ptr, Имейте в виду, что std::optional является частью C ++ 17, поэтому, если вы можете использовать его, в основном зависит от того, какой редакции стандарта вы должны придерживаться.

2

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

Не зная Зачем Вы хотите сделать это, мое единственное предложение — расстаться Big в части, которые должны быть уничтожены после Small от остальных, а затем использовать композицию, чтобы включить это внутри Big, Тогда у вас есть контроль над порядком уничтожения:

class Small
{
public:
~Small() {std::cout << "Small destructor" << std::endl;}
};

class BigImpl
{
public:
~BigImpl() { std::cout << "Big destructor" << std::endl; }
};

class Big
{
private:
BigImpl bigimpl;
Small small;
};
1

Порядок вызовов деструкторов не может быть изменен. Правильный способ разработать это, что Small выполняет свою собственную очистку.

Если вы не можете изменить Small тогда вы могли бы сделать класс SmallWrapper который содержит Small а также может выполнить необходимую очистку.

Стандартные контейнеры std::optional или же std::unique_ptr или же std::shared_ptr может быть достаточно для этой цели.

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