(C++) 调用析构函数时的子类链销毁

(c++) subclass chain destruction when calling destructor

本文关键字:子类 C++ 调用 析构函数      更新时间:2023-10-16
class A { 
....
};
class B: public A {
int x;
...
};
class C: public A{
A * ptr;
.....
};
class D : public A {
A* ptr1;
A* ptr2;
....
};

注意:我为 B、C、D 制作了所有构造函数,只是没有将它们包含在其中。所以 A(没有字段)是超类,我有 3 个子类(B、C 和 D),每个子类都有不同的字段。

A 是一个抽象类,它主要链接类 (B,C,D)

所以就像我可能有这样的情况

B *x = new B {5};
B *x2 = new B {5}
D * y = new D{x,x2);

因此,当我执行delete y;时,我想让它链式破坏其两个字段的 2 个指针,即(B 对象)。如何使 D 类的析构函数然后链式析构函数?

就像我展示的示例非常简单,但其他示例具有越来越多的层。我想确保删除所有内容,以免发生内存泄漏。

我的 D 类 dtor 应该看起来像这样吗?

~D(){
delete ptr1;
delete ptr2;
}

对于C类的情况,我会这样做吗?

~C(){
    delete ptr;
    }

因为我这样做了,但它不起作用,我得到了内存泄漏,所以有什么问题?

我认为你的问题是错误的,因为你说这是一个继承链,但在撰写B时,CD直接来自A

当你做new D时,你有一个由ABCD组成的复合对象——它们是按继承顺序构造的。

*y = {
    // B starts
    int x;
    // C starts
    A * ptr;
    // D starts
    A * ptr1;
    A * ptr2;
};

请注意,析构函数将以相反的顺序调用,D先调用,然后C,依此类推,以相反的顺序清理内存(以处理对超类的依赖关系)。但是,这取决于您要删除的对象类型。

如果我们这样做:

D * y = new D;
A * x = y;
delete x;

我们有效地将y视为A对象,这意味着我们仅限于A对象知道的内容,只要我们使用x就可以执行。如果我们使用 x 删除 ,我们将调用~A()而不是~D() 。这就是为什么在这种情况下析构函数必须是虚拟的,这允许virtual ~A()调用~D()最终将再次调用~A()(真实的而不是虚拟发送)。

无论您是否使用多态/上转换,您的直觉都是正确的 - ~D()应该只清理D对象引入类(ptr1ptr2)的内存,同样适用于~C()(仅ptr)。

泄漏可能是由于某处发生了对象切片。例如(如果你强制编译它),这比上面的例子更糟糕,因为对象数据不会被复制(与结构定义相对于所引用的对象类型是不完整的相反):

D d;
C * c = d; // not too bad, we can't refer to D-specific things.
C c2 = d; // bad, c2 is copied from D up to C superclass and no further.

请注意,仅将指针/引用传递给可能需要保存派生类型的对象,并确保使用虚拟析构函数。否则,您正在执行的操作是正确的,让每个类的析构函数只针对该类进行清理。