面试题答案
一键面试动态绑定的实现方式
在C++ 中,动态绑定是通过虚函数和指针或引用实现的。当通过指针或引用调用虚函数时,C++ 运行时系统会在运行时根据对象的实际类型来决定调用哪个函数版本,而不是根据指针或引用的静态类型。
涉及的底层数据结构
- 虚函数表(Virtual Table,vtable):每个包含虚函数的类都有一个虚函数表。虚函数表是一个数组,其中存储了该类所有虚函数的地址。如果一个类从基类继承了虚函数,虚函数表中相应的条目会指向被覆盖的虚函数(如果有),否则指向基类的虚函数。
- 虚函数表指针(vptr):每个包含虚函数的对象都有一个虚函数表指针。这个指针指向该对象所属类的虚函数表。通常,vptr 被放置在对象内存布局的开头位置。
数据结构的协同工作
- 编译期:编译器为每个包含虚函数的类生成虚函数表,并在对象的构造函数中初始化 vptr,使其指向该类的虚函数表。
- 运行期:当通过指针或引用调用虚函数时,运行时系统首先通过对象的 vptr 找到对应的虚函数表。然后,根据虚函数在虚函数表中的索引,找到要调用的实际函数的地址,并执行该函数。
例如,假设有如下继承体系:
class Base {
public:
virtual void foo() {
std::cout << "Base::foo" << std::endl;
}
};
class Derived : public Base {
public:
void foo() override {
std::cout << "Derived::foo" << std::endl;
}
};
在 main
函数中:
int main() {
Base* basePtr = new Derived();
basePtr->foo(); // 动态绑定,调用 Derived::foo
delete basePtr;
return 0;
}
在上述代码中,basePtr
是 Base*
类型,但实际指向 Derived
对象。当调用 basePtr->foo()
时,运行时系统通过 basePtr
所指向对象的 vptr 找到 Derived
类的虚函数表,进而调用 Derived::foo
。