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

时间:2017-07-26 15:24:16

标签: c++ destructor object-destruction

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

假设我有2个类<audio controls> <source src="http://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"</source> </audio> <audio controls> <source src="http://localhost:8080/MyProject/static/music/SoundHelix-Song-1.mp3"></source> </audio> ,而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析构函数执行了必要的清理工作。

我可以:

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

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

    有没有更好的方法呢?

3 个答案:

答案 0 :(得分:2)

  

显式调用small .~Small()析构函数。 - &GT;但是,这会将小析构函数调用两次:一次是显式的,一次是在执行大型析构函数之后。

好吧,我不知道为什么你要继续使用这个瑕疵设计,但是你可以使用新的位置解决你的第一个项目中描述的问题
它遵循一个最小的工作示例:

#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的一部分,因此如果您可以使用它,那么主要取决于您必须遵守的标准版本。

答案 1 :(得分:1)

在不知道为什么你想要这样做的情况下,我唯一的建议是将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;
};

答案 2 :(得分:0)

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

如果您无法更改Small,则可以创建包含SmallWrapper的课程Small,并且还可以执行所需的清理。

标准容器std::optionalstd::unique_ptrstd::shared_ptr可能足以达到此目的。