面试题答案
一键面试对象的内存布局
- 基类对象:若基类包含虚函数,其对象内存布局通常在起始位置会有一个虚指针(vptr),该指针指向虚函数表(vtable)。之后是基类的其他成员变量,按照声明顺序排列。
- 派生类对象:派生类对象首先包含基类部分的内存布局(同样在起始位置有指向基类虚函数表的虚指针,如果基类有虚函数),然后是派生类自己新增的成员变量,同样按照声明顺序排列。如果派生类重写了基类的虚函数,虚函数表中的对应条目会被替换为派生类重写后的函数地址。
虚函数表的作用
- 实现多态调用:虚函数表是一个函数指针数组,每个条目存储了虚函数的地址。当通过基类指针或引用调用虚函数时,程序会根据对象的实际类型(通过虚指针找到对应的虚函数表),在虚函数表中查找并调用正确的函数。例如,假设有一个
Base* ptr
,如果ptr
指向一个Derived1
对象,那么ptr->虚函数()
调用时,会通过Derived1
对象的虚指针找到Derived1
的虚函数表,进而调用Derived1
中重写(如果有)的虚函数。 - 运行时确定函数调用:它使得程序在运行时能够根据对象的实际类型来确定调用哪个虚函数,而不是在编译时就固定下来,从而实现了多态。
虚函数表与C++多态两个必要条件的关联
C++多态的两个必要条件是:继承和虚函数重写。
- 继承:通过继承,派生类获得了基类的虚函数表指针(如果基类有虚函数),从而可以利用虚函数表来实现多态。派生类对象的内存布局中包含基类部分,使得派生类对象可以像基类对象一样被处理,为多态调用提供了基础。
- 虚函数重写:当派生类重写基类的虚函数时,虚函数表中的相应条目会被更新为派生类重写后的函数地址。这样,在运行时通过虚函数表,就能够根据对象的实际类型调用到正确的重写后的虚函数,实现了运行时多态。如果没有虚函数重写,虚函数表中的条目不会改变,多态调用的效果就无法体现。