C++ 在指针成员之前调用非指针成员的析构函数

C++ 在指针成员之前调用非指针成员的析构函数,c++,pointers,destructor,C++,Pointers,Destructor,考虑以下三个类: class A { int bar; }; class B { A* a; public: B(*A na) : a(na) {} ~B() { a->bar = 0; } }; class Foo { A* a; B b; public: Foo() : a(new A) , b(a) {} ~Foo() { delet

考虑以下三个类:

class A {
    int bar;
};

class B {
    A* a;
public:
    B(*A na)
    : a(na)
    {}

    ~B() {
        a->bar = 0;
    }
};

class Foo {
    A* a;
    B b;
public:
    Foo()
    : a(new A)
    , b(a)
    {}

    ~Foo() {
        delete a;
    }
};
创建
Foo
的实例,会产生一个指向
a
Foo
,以及一个指向同一指针的
B

Foo
的实例被删除时,指针
a
被删除,然后调用
b
的析构函数,但是
~b
试图访问
a
,而
~Foo
中释放了
a,这导致分段错误

在运行
~Foo
主体之前,是否有方法调用
b
的析构函数

我知道我可以通过使
b
成为指针或使
a
成为非指针来解决这个问题。如果
A
是抽象的,则后者不起作用,我想知道是否可以不使用
b
指针

注:
A
B
都来自一个库,所以我不能更改它们的实现。

编辑:更正错误(谢谢@Yakk)


您需要在
Foo
中设置
B
指针,以便可以控制析构函数中的删除顺序,或者将
a
设置为非指针,并按照
Foo
的成员顺序将其放置在
B
之前,以便在
B
之后将其销毁,但这使得代码变得脆弱,因为成员声明的顺序现在很重要。在代码中对此进行注释。

创建一个中间类

 class PreFoo 
 {
   A* a:
    ...
 };

 class Foo : public PreFoo
 {
   B b;
   ...
 };

遵循RAII原则

Foo::a
更改为
std::unique\u ptr
。从
~Foo
中删除
delete

它现在将在
~Foo
b.~Bar()
结束后销毁


请注意,成员按声明顺序创建,并按相反顺序销毁。(令人惊讶的是,初始化列表中的顺序不会更改创建顺序)。因此,如果你想
a
b
长寿,只需先声明它。

如我在问题中所述,我知道这一点,但想知道不这样做是否可行。(我编辑这个问题是为了让这个限制更清楚,以防不够清楚)@Kritzefitz你必须对
Foo
进行一些修改,唯一的问题是什么..当然我必须改变一些东西。但我不知道除了我排除的两个选项之外还有什么选择。也许还有更优雅的解决方案,我不知道。你需要将
a
设置为受保护或公共,我建议将私有继承设置为与原始设计相匹配。我认为当你说“making
a
a指针”时缺少的单词(你忘记键入)是“not”,但缺少的单词应该是“smart”。清晰的答案是
a
应该是一个智能指针,因此它会按正确的顺序删除。这并不能真正回答您的问题,但我建议将B作为指针。你可以通过使用智能指针来完成你的要求,但我认为依赖基于成员顺序的销毁顺序是一种危险的做法,除非你有很好的理由,我不认为这是一种危险的做法。最好是把它们都变成指针,然后按正确的顺序解构,并用注释解释为什么它们必须按那个顺序解构。@Gerald,设计显然必须取决于构造的顺序。使用智能指针不会对序列产生新的依赖。它将只依赖于销毁顺序与构造顺序相反,而构造顺序是语言中内置的规则(除非您强制执行其他操作,就像OP使用非智能指针所做的那样)。@JSF-如果ctor/dtor的顺序像这样重要,我宁愿将其显式化,而不是依赖于成员顺序。是的,规则是内置在语言中的,但这并不意味着你想依赖它来方便。在团队环境中,尤其是在这种设计中,你只会自找麻烦。i、 有一天,可能会出现一种编码标准,即成员应该按照字母顺序进行声明,甚至可能他们会使用一种工具使代码达到标准,而这将打破,谁知道需要多长时间才能弄清楚发生了什么。(去过那里。)如果他们真的被命名为A和B,他会没事的,但不知何故我怀疑:)