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