MST

星途 面试题库

面试题:C++ 类继承体系下对象的内存布局与多态实现

考虑一个C++类继承体系,基类中有虚函数,派生类重写了该虚函数。请详细描述在创建派生类对象时,其内存布局是怎样的,以及虚函数表(vtable)和虚函数指针(vptr)在多态实现过程中是如何工作的。
13.7万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

派生类对象内存布局

  1. 基类部分:派生类对象首先包含基类的所有成员(包括数据成员和成员函数指针等),按照基类定义的顺序存储在内存中。如果基类有虚函数,那么基类部分会有一个虚函数指针 vptr,它指向基类的虚函数表 vtable
  2. 派生类新增部分:在基类部分之后,接着存储派生类新增的数据成员,按照派生类定义的顺序排列。如果派生类重写了基类的虚函数,虚函数表中的对应条目会被替换为派生类中重写的虚函数的地址;若派生类有新增的虚函数,则虚函数表会在原有基础上扩充,增加对应新虚函数的条目。

虚函数表(vtable)和虚函数指针(vptr)在多态实现过程中的工作原理

  1. 虚函数表(vtable)
    • 每个包含虚函数的类都有一个虚函数表。虚函数表是一个数组,数组中的每个元素是一个函数指针,指向类中的虚函数。对于基类和派生类,它们各自有自己的虚函数表。如果派生类重写了基类的虚函数,派生类虚函数表中对应基类虚函数的条目会指向派生类重写后的虚函数地址。如果派生类有新的虚函数,虚函数表会在末尾添加新的条目指向这些新虚函数。
  2. 虚函数指针(vptr)
    • 每个包含虚函数的对象都有一个虚函数指针 vptr。在对象构造过程中,编译器会自动将 vptr 初始化为指向该对象所属类的虚函数表。对于派生类对象,vptr 指向派生类的虚函数表。
  3. 多态实现
    • 当通过基类指针或引用调用虚函数时,运行时系统首先根据对象的 vptr 找到对应的虚函数表。然后,根据虚函数在虚函数表中的索引(即偏移量),找到要调用的实际虚函数的地址,并执行该函数。这使得程序能够在运行时根据对象的实际类型来决定调用哪个版本的虚函数,从而实现多态。例如:
class Base {
public:
    virtual void virtualFunction() {
        std::cout << "Base::virtualFunction" << std::endl;
    }
};

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

int main() {
    Base* basePtr = new Derived();
    basePtr->virtualFunction(); // 通过 vptr 和 vtable 调用 Derived::virtualFunction
    delete basePtr;
    return 0;
}

在上述代码中,basePtr 是基类指针,但指向的是派生类对象。当调用 virtualFunction 时,程序通过 basePtr 所指对象的 vptr 找到派生类的虚函数表,再根据虚函数表找到 Derived::virtualFunction 并执行。