面试题答案
一键面试虚函数表布局和调用机制
在C++多重继承中,当一个类继承自多个包含虚函数的基类时:
- 虚函数表布局:每个基类会有自己的虚函数表(vtable)。派生类对象的内存布局中,会为每个基类的虚函数表分配空间。这些虚函数表按继承顺序排列,每个虚函数表中存储着对应基类虚函数的地址。
- 调用机制:当通过派生类对象指针或引用调用虚函数时,编译器根据对象的内存布局找到对应的虚函数表,然后在虚函数表中找到对应的虚函数地址并调用。
代码示例
#include <iostream>
class Base1 {
public:
virtual void func1() { std::cout << "Base1::func1" << std::endl; }
};
class Base2 {
public:
virtual void func2() { std::cout << "Base2::func2" << std::endl; }
};
class Derived : public Base1, public Base2 {
public:
void func1() override { std::cout << "Derived::func1" << std::endl; }
void func2() override { std::cout << "Derived::func2" << std::endl; }
};
内存布局分析
- 使用工具:在GCC下,可以使用
objdump -d -S
命令反汇编可执行文件来查看虚函数表相关信息。 - 内存布局:
Derived
对象的内存布局首先是Base1
的部分,其中包含指向Base1
虚函数表的指针(vptr),这个虚函数表中func1
的地址是Derived::func1
的地址(因为Derived
重写了func1
)。- 接着是
Base2
的部分,包含指向Base2
虚函数表的指针,此虚函数表中func2
的地址是Derived::func2
的地址(因为Derived
重写了func2
)。 - 例如,当通过
Derived
对象指针调用func1
时,会先找到Base1
虚函数表指针,再在Base1
虚函数表中找到func1
的地址并调用Derived::func1
。调用func2
类似,通过Base2
虚函数表指针找到func2
地址调用Derived::func2
。