Есть ли способ вызвать деструктор поля перед деструктором класса?
Предположим, у меня есть 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
деструктор.
Я мог бы:
small.~Small()
деструктор явно. -> Это, однако, вызывает Small
деструктор дважды: один раз явно, и один раз после Big
деструктор был казнен.Small*
как поле и вызов delete small;
в Big
деструктор Я знаю, что я могу иметь функцию в Small
класс, который делает уборку и вызывает его в Big
деструктор, но мне было интересно, есть ли способ изменить порядок деструктора.
Есть ли лучший способ сделать это?
вызовите деструктор 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 char
s.
Кроме того, вы не будете хранить свои Small
непосредственно в динамическое хранилище, так как на самом деле вы используете элемент данных вашего Big
создать его в.
При этом я бы посоветовал вам разместить его в динамическом хранилище, если у вас нет веских причин поступить иначе. Использовать std::unique_ptr
и сбросить его в начале деструктора Big
, Ваш Small
уйдет до того, как тело деструктора будет действительно выполнено, как и ожидалось, и в этом случае деструктор не будет вызван дважды.
РЕДАКТИРОВАТЬ
Как предлагается в комментариях, std::optional
может быть другим жизнеспособным решением вместо std::unique_ptr
, Имейте в виду, что std::optional
является частью C ++ 17, поэтому, если вы можете использовать его, в основном зависит от того, какой редакции стандарта вы должны придерживаться.
Не зная Зачем Вы хотите сделать это, мое единственное предложение — расстаться 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;
};
Порядок вызовов деструкторов не может быть изменен. Правильный способ разработать это, что Small
выполняет свою собственную очистку.
Если вы не можете изменить Small
тогда вы могли бы сделать класс SmallWrapper
который содержит Small
а также может выполнить необходимую очистку.
Стандартные контейнеры std::optional
или же std::unique_ptr
или же std::shared_ptr
может быть достаточно для этой цели.