题目

编写程序,设计具有多层派生关系(基类 Grandparent、父类 Parent、子类 Child)且包含成员对象成员的完整类继承树。精确打印并追踪当实例化最底层子类对象以及该对象离开作用域被销毁时:构造函数“先基类、再成员对象、最后派生类”的自顶向下构造调用链,以及析构函数严格呈完全镜像逆序“先派生类、再成员对象、最后基类”的自底向上析构释放链。

输入格式:无需外部输入。

输出格式:输出多级继承体系下构造链与析构链的对称控制台调用日志。

数据范围与约定:OOP 构造逆序对称性原理、依赖倒置与内存装配机制。

思路与算法

1. 为什么构造必须先基后派
派生类是在基类的骨架基础上扩充细节。派生类的构造函数中极可能需要调用基类的方法或访问基类的成员,因此在子类构造执行时,基类部分必须已经完全在内存中装配就绪。

2. 为什么析构必须先派后基
派生类析构函数执行时,可能需要清理依赖基类特性的资源;若基类先析构,派生类清理时访问已被破坏的基类数据会导致未定义行为。因此必须由外至内、先拆派生再拆基类。

3. 成员对象与基类的次序
构造次序:①所有虚拟基类;②所有直接非虚基类(按继承声明顺序);③类内成员对象(按声明顺序);④派生类自身构造函数体。

完整程序

#include <iostream>

class BasePart {
public:
    BasePart()  { std::cout << "  (1) [构造] BasePart (基类)\n"; }
    ~BasePart() { std::cout << "  (6) [析构] BasePart (基类)\n"; }
};

class MemberComponent {
public:
    MemberComponent()  { std::cout << "  (2) [构造] MemberComponent (派生类内部成员)\n"; }
    ~MemberComponent() { std::cout << "  (5) [析构] MemberComponent (派生类内部成员)\n"; }
};

class DerivedObject : public BasePart {
private:
    MemberComponent comp_; // 成员对象
public:
    DerivedObject()  { std::cout << "  (3) [构造] DerivedObject (派生类主体自身)\n"; }
    ~DerivedObject() { std::cout << "  (4) [析构] DerivedObject (派生类主体自身)\n"; }
};

int main() {
    std::cout << "=== 1. 实例化 DerivedObject 对象,观察构造顺序 ===\n";
    {
        DerivedObject obj;
        std::cout << "\n=== 对象生命周期正常运作中 ===\n\n";
        std::cout << "=== 2. 离开作用域,观察析构严格镜像逆序 ===\n";
    }
    std::cout << "所有层次组件均已安全反向析构完毕\n";

    return 0;
}

运行示例

输出:

=== 1. 实例化 DerivedObject 对象,观察构造顺序 ===
  (1) [构造] BasePart (基类)
  (2) [构造] MemberComponent (派生类内部成员)
  (3) [构造] DerivedObject (派生类主体自身)

=== 对象生命周期正常运作中 ===

=== 2. 离开作用域,观察析构严格镜像逆序 ===
  (4) [析构] DerivedObject (派生类主体自身)
  (5) [析构] MemberComponent (派生类内部成员)
  (6) [析构] BasePart (基类)
所有层次组件均已安全反向析构完毕