面试题答案
一键面试虚函数表在函数指针动态绑定机制中的角色
在 C++ 中,虚函数表(vtable)是实现函数指针动态绑定(运行时多态)的关键机制。当一个类包含虚函数时,编译器会为该类创建一个虚函数表。虚函数表本质上是一个函数指针数组,数组中的每个元素指向该类的一个虚函数的实现。
每个包含虚函数的对象都包含一个指向其所属类的虚函数表的指针(vptr)。当通过基类指针或引用调用虚函数时,程序会根据对象的实际类型(通过 vptr 找到对应的虚函数表),在运行时决定调用哪个虚函数的实现,从而实现动态绑定。
虚函数表的结构
- 每个包含虚函数的类都有一个虚函数表:虚函数表是一个静态数组,存储在程序的只读数据段(常量区)。
- 虚函数表中的元素:每个元素是一个函数指针,指向对应虚函数的实现。虚函数表中函数指针的顺序与虚函数在类中声明的顺序一致。
- 派生类的虚函数表:如果一个派生类继承自一个包含虚函数的基类,它会继承基类的虚函数表,并根据需要对其进行修改。如果派生类重写了基类的某个虚函数,那么在派生类的虚函数表中,对应位置的函数指针会被替换为派生类中该虚函数的新实现。如果派生类定义了新的虚函数,这些新虚函数的函数指针会被添加到虚函数表的末尾。
虚函数表协助实现函数指针动态绑定的过程
- 对象创建时:当创建一个包含虚函数的对象时,编译器会在对象的内存布局开头(通常情况)插入一个 vptr,指向该对象所属类的虚函数表。
- 函数调用时:当通过基类指针或引用调用虚函数时,程序首先通过对象的 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;
}
在上述代码中:
Base
类包含一个虚函数func
。Derived1
类继承自Base
类并重写了func
函数。Derived2
类继承自Derived1
类并重写了func
函数。- 在
main
函数中,通过Base
类指针分别指向不同类型的对象,并调用func
函数。由于虚函数表和动态绑定机制,程序会在运行时根据对象的实际类型调用正确的虚函数实现。