C++销毁顺序:在类析构函数之前调用字段析构函数

CK.*_*CK. 6 c++ destructor object-destruction

有没有办法在类析构函数之前调用字段析构函数?

假设我有 2 个类SmallBig,并且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;
}
Run Code Online (Sandbox Code Playgroud)

当然,这在小析构函数之前调用大析构函数:

Big destructor
Small destructor
Run Code Online (Sandbox Code Playgroud)

我需要在Small析构函数之前调用Big析构函数,因为它为Big析构函数做了一些必要的清理工作。

我可以:

  1. small.~Small()显式调用析构函数。-> 然而,这Small两次调用析构函数:一次显式调用,一次在Big析构函数执行后。
  2. Small*作为的领域,并呼吁delete small;Big析构函数

我知道我可以在Small类中有一个函数来进行清理并在Big析构函数中调用它,但我想知道是否有办法反转析构函数的顺序。

有没有更好的方法来做到这一点?

sky*_*ack 2

\n

显式调用\xc2\xa0small.~Small()\xc2\xa0析构函数。-> 但是,这会调用小析构函数两次:一次是显式调用,另一次是在执行大析构函数之后。

\n
\n\n

好吧,我不知道您为什么要继续使用这种有缺陷的设计,但是您可以使用新的放置来解决第一个项目符号中描述的问题。
\n它遵循一个最小的工作示例:

\n\n
#include <iostream>\n\nstruct Small {\n    ~Small() {std::cout << "Small destructor" << std::endl;}\n};\n\nstruct Big {\n    Big() { ::new (storage) Small; }\n\n    ~Big() {\n        reinterpret_cast<Small *>(storage)->~Small();\n        std::cout << "Big destructor" << std::endl;\n    }\n\n    Small & small() {\n        return *reinterpret_cast<Small *>(storage);\n    }\n\nprivate:\n    unsigned char storage[sizeof(Small)];\n};\n\nint main() {\n    Big big;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

您不再有 type 的变量Small,但是使用示例中的成员函数之类的东西,small您可以轻松解决它。

\n\n

这个想法是,您保留足够的空间来构造就地 a Small,然后您可以像您一样显式调用其析构函数。它不会被调用两次,因为该类Big必须释放的只是一个 s 数组unsigned char
\n此外,您不会Small直接将您存储到动态存储中,因为实际上您正在使用您的数据成员Big来创建它。

\n\n
\n\n

话虽这么说,我建议您将其分配在动态存储上,除非您有充分的理由不这样做。使用 astd::unique_ptr并在 的析构函数的开头重置它Big。在析构函数的主体实际按预期执行之前,您的Small意志就会消失,而且在这种情况下,析构函数不会被调用两次。

\n\n
\n\n

编辑

\n\n

正如评论中所建议的,std::optional可以是另一种可行的解决方案而不是std::unique_ptr. 请记住,这std::optional是 C++17 的一部分,因此您是否可以使用它主要取决于您必须遵守的标准的修订版本。

\n

  • 一般来说,“存储”可能无法正确对齐“小”。您应该使用“std::aligned_storage” (2认同)