面试题答案
一键面试1. FinalDerived
对象中虚函数表的结构
在这种菱形继承结构下,FinalDerived
对象通常会有两个虚函数表指针(vptr),分别对应从Derived1
和Derived2
继承而来的部分。
- 第一个虚函数表(对应
Derived1
路径):开头存放的是Derived1::baseFunc
的地址,因为FinalDerived
重写了该虚函数,实际存放的是FinalDerived::baseFunc
的地址。 - 第二个虚函数表(对应
Derived2
路径):同样,开头存放的也是FinalDerived::baseFunc
的地址,因为虚函数重写的机制使得无论从哪个继承路径看,最终调用的都是FinalDerived
重写后的版本。
2. 多重继承和菱形继承在虚函数重写与多态调用方面带来的复杂问题
- 二义性问题:当通过
FinalDerived
对象调用baseFunc
时,由于存在两条继承路径(Derived1
和Derived2
),编译器不知道应该使用哪个路径的虚函数表中的baseFunc
,从而产生二义性。例如,FinalDerived fd; fd.baseFunc();
这样的调用会报错。 - 数据冗余问题:在菱形继承中,
Base
类的成员在FinalDerived
对象中会被重复继承两份(分别来自Derived1
和Derived2
),这不仅浪费内存,还可能导致数据不一致等问题。
3. 解决这些问题的方法
- 虚继承:在
Derived1
和Derived2
继承Base
时使用虚继承(class Derived1 : virtual public Base
和class Derived2 : virtual public Base
)。这样FinalDerived
对象中只会保留一份Base
类的成员,避免数据冗余。同时,虚继承也会调整虚函数表结构,使得通过FinalDerived
对象调用baseFunc
不会产生二义性,编译器能够明确知道应该调用哪个版本的虚函数。 - 明确指定路径:在调用虚函数时明确指定使用哪个继承路径的虚函数表。例如,
fd.Derived1::baseFunc();
或fd.Derived2::baseFunc();
这样可以避免二义性,但这种方式不够优雅,且破坏了多态的灵活性。