MST

星途 面试题库

面试题:C++中构造函数与析构函数调用顺序在复杂继承与多态场景下的表现

考虑以下C++代码: ```cpp class Base { public: Base() { std::cout << "Base constructor" << std::endl; } virtual ~Base() { std::cout << "Base destructor" << std::endl; } }; class Derived1 : public Base { public: Derived1() { std::cout << "Derived1 constructor" << std::endl; } ~Derived1() { std::cout << "Derived1 destructor" << std::endl; } }; class Derived2 : public Derived1 { public: Derived2() { std::cout << "Derived2 constructor" << std::endl; } ~Derived2() { std::cout << "Derived2 destructor" << std::endl; } }; ``` 如果有代码 `Base* ptr = new Derived2(); delete ptr;`,输出结果是什么,解释为什么会是这样的输出顺序,特别是析构函数的调用顺序有什么特点,这种顺序在实际编程中有什么意义。
14.3万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
  1. 输出结果
    Base constructor
    Derived1 constructor
    Derived2 constructor
    Derived2 destructor
    Derived1 destructor
    Base destructor
    
  2. 解释输出顺序
    • 构造函数顺序:当执行Base* ptr = new Derived2();时,由于继承关系,会先调用基类的构造函数。Derived2继承自Derived1Derived1又继承自Base,所以先调用Base的构造函数,输出Base constructor;接着调用Derived1的构造函数,输出Derived1 constructor;最后调用Derived2的构造函数,输出Derived2 constructor
    • 析构函数顺序:当执行delete ptr;时,因为Base类的析构函数是virtual的,这使得通过基类指针删除派生类对象时,会调用正确的析构函数。先调用最派生类Derived2的析构函数,输出Derived2 destructor;然后调用直接基类Derived1的析构函数,输出Derived1 destructor;最后调用Base类的析构函数,输出Base destructor
  3. 析构函数调用顺序特点
    • 析构函数的调用顺序与构造函数的调用顺序相反。这是因为构造函数是从基类到派生类依次构建对象的状态,而析构函数则是从最派生类到基类依次清理对象的状态。
    • 当通过基类指针删除派生类对象时,若基类析构函数是virtual的,C++会按照继承层次从最派生类到基类依次调用析构函数,以确保对象的所有部分都被正确清理。
  4. 在实际编程中的意义
    • 资源清理:在实际编程中,对象通常会持有资源(如内存、文件句柄、网络连接等)。按照这种顺序调用析构函数,能保证资源以正确的顺序被释放。例如,如果派生类对象分配了一些特定资源,而基类对象也有相关资源,先析构派生类可以释放其特有的资源,再析构基类可以释放基类相关的资源,避免资源泄漏。
    • 维护对象完整性:这种顺序有助于维护对象状态的完整性。从最派生类开始析构,确保了对象的特定状态首先被清理,然后逐步清理基类状态,符合对象创建和销毁的逻辑过程。