MST

星途 面试题库

面试题:C++ 多态中对象特征的深入应用

在 C++ 中,动态多态性依赖于虚函数表(vtable)。请详细说明虚函数表是如何在编译期和运行期进行构建与维护的,并且分析当存在多重继承和虚继承时,虚函数表的结构会发生怎样的变化。
23.2万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

编译期构建

  1. 类定义时生成:当一个类中声明了虚函数,编译器会为该类生成虚函数表。虚函数表是一个指针数组,每个元素是一个指向虚函数实现的指针。例如:
class Base {
public:
    virtual void func() {}
};

编译器会为Base类生成一个虚函数表,表中包含func函数的指针。 2. 继承关系处理:如果一个派生类继承自含有虚函数表的基类,派生类会继承基类的虚函数表,并对其进行修改。如果派生类重写了基类的虚函数,虚函数表中对应虚函数的指针会被替换为派生类中该虚函数的实现地址。例如:

class Derived : public Base {
public:
    void func() override {}
};

Derived类的虚函数表中,func函数的指针会指向Derived::func的实现。

运行期维护

  1. 对象创建:当创建一个对象时,编译器会在对象的内存布局中添加一个虚函数表指针(vptr),该指针指向对应的虚函数表。例如,创建Base类对象:
Base b;

b对象的内存布局开头会有一个vptr,指向Base类的虚函数表。 2. 动态绑定:当通过基类指针或引用调用虚函数时,运行时系统会根据对象的vptr找到对应的虚函数表,然后根据虚函数表中函数指针来调用实际的函数。例如:

Base* ptr = new Derived();
ptr->func();

运行时,ptr指向Derived对象,通过ptr中的vptr找到Derived类的虚函数表,进而调用Derived::func

多重继承时虚函数表结构变化

  1. 多个虚函数表:在多重继承下,派生类可能会有多个基类,每个基类如果有虚函数,派生类都会继承对应的虚函数表。例如:
class Base1 {
public:
    virtual void func1() {}
};
class Base2 {
public:
    virtual void func2() {}
};
class Derived : public Base1, public Base2 {
public:
    void func1() override {}
    void func2() override {}
};

Derived类会有两个虚函数表,一个继承自Base1,一个继承自Base2。对象内存布局中会有两个vptr,分别指向这两个虚函数表。 2. 函数覆盖与查找:派生类重写的虚函数会在对应的基类虚函数表中更新指针。调用虚函数时,根据对象指针的类型确定使用哪个虚函数表来查找函数地址。例如通过Base1*指针调用func1,会使用Base1对应的虚函数表。

虚继承时虚函数表结构变化

  1. 共享虚基类:虚继承用于解决菱形继承中的数据冗余和二义性问题。当存在虚继承时,虚基类的虚函数表指针会被合并到派生类的虚函数表中。例如:
class VirtualBase {
public:
    virtual void func() {}
};
class Derived1 : virtual public VirtualBase {};
class Derived2 : virtual public VirtualBase {};
class FinalDerived : public Derived1, public Derived2 {};

FinalDerived类只有一个VirtualBase子对象,其虚函数表会包含指向VirtualBase虚函数的指针,并且虚函数表中还会包含偏移量等信息,用于在运行时定位虚基类子对象。 2. 偏移量计算:为了在多重继承层次结构中正确访问虚基类子对象,虚函数表中会存储一些偏移量信息。这些偏移量用于在运行时计算对象中虚基类子对象的地址,确保正确调用虚基类的虚函数。