MST

星途 面试题库

面试题:C++ 虚函数表与函数指针动态绑定的关系

在 C++ 中,虚函数表在函数指针动态绑定机制中扮演着怎样的角色?请详细阐述虚函数表的结构以及它如何协助实现函数指针的动态绑定。并且通过代码示例说明当类继承体系中存在多层继承和虚函数重写时,函数指针动态绑定的具体过程。
37.6万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

虚函数表在函数指针动态绑定机制中的角色

在 C++ 中,虚函数表(vtable)是实现函数指针动态绑定(运行时多态)的关键机制。当一个类包含虚函数时,编译器会为该类创建一个虚函数表。虚函数表本质上是一个函数指针数组,数组中的每个元素指向该类的一个虚函数的实现。

每个包含虚函数的对象都包含一个指向其所属类的虚函数表的指针(vptr)。当通过基类指针或引用调用虚函数时,程序会根据对象的实际类型(通过 vptr 找到对应的虚函数表),在运行时决定调用哪个虚函数的实现,从而实现动态绑定。

虚函数表的结构

  1. 每个包含虚函数的类都有一个虚函数表:虚函数表是一个静态数组,存储在程序的只读数据段(常量区)。
  2. 虚函数表中的元素:每个元素是一个函数指针,指向对应虚函数的实现。虚函数表中函数指针的顺序与虚函数在类中声明的顺序一致。
  3. 派生类的虚函数表:如果一个派生类继承自一个包含虚函数的基类,它会继承基类的虚函数表,并根据需要对其进行修改。如果派生类重写了基类的某个虚函数,那么在派生类的虚函数表中,对应位置的函数指针会被替换为派生类中该虚函数的新实现。如果派生类定义了新的虚函数,这些新虚函数的函数指针会被添加到虚函数表的末尾。

虚函数表协助实现函数指针动态绑定的过程

  1. 对象创建时:当创建一个包含虚函数的对象时,编译器会在对象的内存布局开头(通常情况)插入一个 vptr,指向该对象所属类的虚函数表。
  2. 函数调用时:当通过基类指针或引用调用虚函数时,程序首先通过对象的 vptr 找到对应的虚函数表,然后根据虚函数在虚函数表中的索引(基于其在类中声明的顺序),找到并调用相应的虚函数实现。

代码示例

#include <iostream>

class Base {
public:
    virtual void func() {
        std::cout << "Base::func()" << std::endl;
    }
};

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

class Derived2 : public Derived1 {
public:
    void func() override {
        std::cout << "Derived2::func()" << std::endl;
    }
};

int main() {
    Base* basePtr;

    Base baseObj;
    basePtr = &baseObj;
    basePtr->func();  // 调用 Base::func()

    Derived1 derived1Obj;
    basePtr = &derived1Obj;
    basePtr->func();  // 调用 Derived1::func()

    Derived2 derived2Obj;
    basePtr = &derived2Obj;
    basePtr->func();  // 调用 Derived2::func()

    return 0;
}

在上述代码中:

  1. Base 类包含一个虚函数 func
  2. Derived1 类继承自 Base 类并重写了 func 函数。
  3. Derived2 类继承自 Derived1 类并重写了 func 函数。
  4. main 函数中,通过 Base 类指针分别指向不同类型的对象,并调用 func 函数。由于虚函数表和动态绑定机制,程序会在运行时根据对象的实际类型调用正确的虚函数实现。