面试题答案
一键面试MultipleDerived类对象内存布局中虚函数表的存储位置
- 虚函数表指针:在C++中,当一个类包含虚函数时,该类对象会包含一个指向虚函数表(vtable)的指针(vptr)。对于
MultipleDerived
类,由于它继承自Derived1
和Derived2
,而Derived1
和Derived2
又继承自带有虚函数的Base
类,所以MultipleDerived
类对象会包含多个虚函数表指针。具体来说,它会有至少两个虚函数表指针,分别对应从Derived1
和Derived2
继承而来的虚函数表。 - 存储位置:通常,这些虚函数表指针存储在
MultipleDerived
类对象内存布局的起始位置(具体位置依赖于编译器实现)。每个虚函数表指针指向一个特定的虚函数表,这些虚函数表存储了该类及其基类虚函数的地址。
通过虚函数表找到对应函数地址的过程
- 调用虚函数:当通过
MultipleDerived
类对象调用虚函数时,首先根据对象类型确定要使用的虚函数表指针。例如,如果调用的虚函数是从Derived1
继承而来的,就使用对应Derived1
的虚函数表指针。 - 查找虚函数表:找到对应的虚函数表指针后,通过该指针访问虚函数表。虚函数表是一个函数指针数组,每个元素存储一个虚函数的地址。
- 确定函数地址:在虚函数表中,根据虚函数在表中的索引找到对应的函数地址。这个索引是在编译时确定的,不同的虚函数有不同的索引值。找到函数地址后,就可以调用相应的虚函数。
菱形继承问题与虚函数表存储的关系
- 菱形继承问题:在上述继承体系中,如果
Base
类被Derived1
和Derived2
继承,MultipleDerived
又继承自Derived1
和Derived2
,就形成了菱形继承。菱形继承会导致MultipleDerived
类对象中存在Base
类成员的两份拷贝,这不仅浪费内存,还可能导致访问歧义。 - 虚函数表存储影响:由于存在两份
Base
类子对象,在虚函数表存储方面,每个Base
类子对象对应的虚函数表可能会有重复的虚函数条目(如果虚函数没有被重新定义)。这增加了虚函数表的大小,并且在调用虚函数时,需要明确指定从哪个Base
类子对象的虚函数表中查找函数地址,否则可能出现歧义。 - 解决方法 - 虚继承:为了解决菱形继承问题,可以使用虚继承。在
Derived1
和Derived2
继承Base
类时使用虚继承(例如class Derived1 : virtual public Base
),这样MultipleDerived
类对象中只会有一份Base
类子对象。在虚继承情况下,虚函数表的结构会更复杂,可能会引入额外的指针来处理虚基类子对象的偏移等信息,但能有效避免菱形继承带来的重复成员和访问歧义问题。