6

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

假设我有 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;
}

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

Big destructor
Small destructor

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

我可以:

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

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

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

4

3 回答 3

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;
};
于 2017-07-26T15:48:53.710 回答
2

显式调用 small.~Small() 析构函数。-> 然而,这会调用小析构函数两次:一次是显式调用,一次是在执行大析构函数之后。

好吧,我不知道您为什么要继续使用这种有缺陷的设计,但是您可以使用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;
}

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

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


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


编辑

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

于 2017-07-26T16:46:08.203 回答
0

析构函数调用的顺序不能更改。设计它的正确方法是Small执行自己的清理。

如果您无法更改Small,那么您可以创建一个SmallWrapper包含 a 的类,Small并且还可以执行所需的清理。

标准容器std::optionalorstd::unique_ptrstd::shared_ptr可能足以满足此目的。

于 2018-03-11T23:02:09.607 回答