C++ 销毁顺序 : Calling a field destructor before the class destructor

标签 c++ destructor object-destruction

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

假设我有 2 个类 SmallBigBig 包含一个 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

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

我可以:

  1. 显式调用 small.~Small() 析构函数。 -> 但是,这会调用 Small 析构函数两次:一次显式调用,一次在 Big 析构函数执行后调用。
  2. 有一个 Small* 作为字段并在 Big 析构函数中调用 delete small;

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

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

最佳答案

call the small.~Small() destructor explicitly. -> This, however, calls the small destructor twice: once explicitly, and once after the big destructor has been executed.

好吧,我不知道你为什么要继续使用这个有缺陷的设计,但你可以使用 placement new 解决第一个项目符号中描述的问题
它遵循一个最小的工作示例:

#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 数组。
此外,您不会将 Small 直接存储到动态存储中,因为实际上您正在使用 Big 的数据成员来创建它。


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


编辑

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

关于C++ 销毁顺序 : Calling a field destructor before the class destructor,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45331271/

相关文章:

c++ - ASIO 直接从异步解析中获取 tcp 端点

c++ - 什么时候成员函数应该同时是 const 和 volatile?

C++ 类初始化语法和错误 "does not name a type"

c++ - 是否在返回之前销毁参数?

c++ - 更改预处理器符号的值以进行测试

c++ - 为什么未命名的 C++ 对象会在作用域 block 结束之前破坏?

C++ 变量不会在范围末尾被删除

c++ - 为什么在 Singleton 的析构函数中调用 exit() 会导致无限循环?

C++:析构函数级联期间的回调和系统计时器事件