MST

星途 面试题库

面试题:C++中构造函数、析构函数与多态性的复杂交互

考虑一个C++类继承体系,基类Base有虚析构函数,派生类Derived重写了一些虚函数。当通过基类指针创建和销毁派生类对象时,分析构造函数和析构函数调用过程中多态性是如何体现的,以及可能会遇到的问题(如内存泄漏等)。请用详细的代码示例阐述,并说明如何避免这些问题。
15.7万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

构造函数和析构函数调用过程中多态性体现

在C++中,构造函数不能是虚函数,所以在构造对象时不存在多态性。但是析构函数可以是虚函数,这在通过基类指针销毁派生类对象时会体现多态性。

当通过基类指针创建派生类对象时,首先会调用基类的构造函数,然后调用派生类的构造函数。而当通过基类指针销毁派生类对象时,如果基类的析构函数是虚函数,会先调用派生类的析构函数,然后调用基类的析构函数,这就是多态性在析构过程中的体现。

代码示例

#include <iostream>

class Base {
public:
    // 虚析构函数
    virtual ~Base() {
        std::cout << "Base destructor" << std::endl;
    }
};

class Derived : public Base {
public:
    ~Derived() override {
        std::cout << "Derived destructor" << std::endl;
    }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;
    return 0;
}

可能遇到的问题 - 内存泄漏

如果基类的析构函数不是虚函数,当通过基类指针销毁派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数。这就可能导致派生类中分配的资源无法释放,从而造成内存泄漏。

示例如下:

#include <iostream>

class Base {
public:
    // 非虚析构函数
    ~Base() {
        std::cout << "Base destructor" << std::endl;
    }
};

class Derived : public Base {
private:
    int* data;
public:
    Derived() {
        data = new int(42);
    }
    ~Derived() override {
        std::cout << "Derived destructor" << std::endl;
        delete data;
    }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;
    return 0;
}

在上述代码中,Derived类在构造函数中分配了内存,在析构函数中释放内存。但由于Base类的析构函数不是虚函数,delete ptr只会调用Base类的析构函数,Derived类的析构函数不会被调用,导致data所指向的内存没有被释放,造成内存泄漏。

如何避免这些问题

为了避免内存泄漏问题,只要一个类有可能会被其他类继承,并且这个类的对象有可能通过基类指针来销毁,那么这个基类就应该有虚析构函数。如第一个代码示例,将Base类的析构函数声明为虚函数,就可以保证在通过基类指针销毁派生类对象时,派生类和基类的析构函数都会被正确调用,从而避免内存泄漏。