面试题答案
一键面试虚函数表布局和工作机制
- 单一继承时虚函数表布局:
- 在单一继承中,当一个类包含虚函数时,会生成一个虚函数表(vtable)。对象内部会有一个虚指针(vptr),指向这个虚函数表。虚函数表是一个函数指针数组,数组中的每个元素对应一个虚函数的地址。
- 例如,有基类
Base
包含虚函数virtual void func1()
,派生类Derived:public Base
重写了func1
。Base
对象的vptr指向Base
类的虚函数表,其中func1
的地址是Base::func1
的地址。Derived
对象的vptr同样指向Derived
类的虚函数表,此时func1
的地址被替换为Derived::func1
的地址。
- 多重继承时虚函数表布局:
- 在多重继承下,派生类会从每个基类继承各自的虚函数表。假设有派生类
Derived
从Base1
、Base2
多重继承,且Base1
和Base2
都有虚函数。 Derived
对象内部会有多个虚指针,分别指向不同基类的虚函数表。例如,Derived
对象中可能先有一个vptr1指向Base1
风格的虚函数表,然后有一个vptr2指向Base2
风格的虚函数表。- 虚函数表中的函数指针排列顺序遵循一定规则。对于从某个基类继承来的虚函数,其在对应基类虚函数表中的位置与在该基类单独存在时虚函数表中的位置一致。如果派生类重写了某个基类的虚函数,那么在对应基类虚函数表中该虚函数位置处会替换为派生类重写版本的函数地址。
- 在多重继承下,派生类会从每个基类继承各自的虚函数表。假设有派生类
不同基类虚函数表之间的关系
- 相互独立:不同基类的虚函数表相互独立,它们之间没有直接的指针关联。每个虚函数表维护自己基类体系下的虚函数地址信息。
- 派生类关联:派生类通过多个虚指针分别与不同基类的虚函数表建立联系。派生类对象在内存中的布局包含多个虚指针,每个虚指针指向对应的基类虚函数表,使得派生类可以通过这些虚函数表调用不同基类的虚函数。
多重继承下虚函数调用过程与单一继承的不同
- 单一继承虚函数调用:
- 在单一继承中,通过对象的虚指针找到虚函数表,然后根据虚函数在虚函数表中的索引找到对应的函数地址并调用。例如,
Base *ptr = new Derived(); ptr->func1();
,ptr
指向Derived
对象,通过Derived
对象的vptr找到Derived
类的虚函数表,再根据func1
在虚函数表中的索引调用Derived::func1
(如果Derived
重写了func1
)。
- 在单一继承中,通过对象的虚指针找到虚函数表,然后根据虚函数在虚函数表中的索引找到对应的函数地址并调用。例如,
- 多重继承虚函数调用:
- 在多重继承下,调用虚函数时首先要确定从哪个基类视角调用。例如,
Base1 *ptr1 = new Derived(); ptr1->func1();
,这里ptr1
是Base1
指针,通过Derived
对象中指向Base1
虚函数表的虚指针找到Base1
风格的虚函数表,然后根据func1
在该虚函数表中的索引调用相应函数。如果Derived
重写了Base1
的func1
,则调用Derived::func1
。 - 而对于
Base2 *ptr2 = new Derived(); ptr2->func2();
(假设func2
是Base2
的虚函数),则通过Derived
对象中指向Base2
虚函数表的虚指针找到Base2
风格的虚函数表并调用相应函数。多重继承下需要根据不同的基类指针来确定使用哪个虚函数表进行虚函数调用,这与单一继承只使用一个虚函数表不同。
- 在多重继承下,调用虚函数时首先要确定从哪个基类视角调用。例如,