MST

星途 面试题库

面试题:C++虚函数重写中的多重继承与菱形继承问题

考虑一个菱形继承结构,`Base`为基类,`Derived1`和`Derived2`继承自`Base`,`FinalDerived`又同时继承自`Derived1`和`Derived2`。`Base`中有一个虚函数`virtual void baseFunc()`,所有派生类都重写了这个函数。请阐述在`FinalDerived`对象中,虚函数表的结构是怎样的?多重继承和菱形继承在虚函数重写与多态调用方面会带来哪些复杂问题?如何解决这些问题?
35.9万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

1. FinalDerived对象中虚函数表的结构

在这种菱形继承结构下,FinalDerived对象通常会有两个虚函数表指针(vptr),分别对应从Derived1Derived2继承而来的部分。

  • 第一个虚函数表(对应Derived1路径):开头存放的是Derived1::baseFunc的地址,因为FinalDerived重写了该虚函数,实际存放的是FinalDerived::baseFunc的地址。
  • 第二个虚函数表(对应Derived2路径):同样,开头存放的也是FinalDerived::baseFunc的地址,因为虚函数重写的机制使得无论从哪个继承路径看,最终调用的都是FinalDerived重写后的版本。

2. 多重继承和菱形继承在虚函数重写与多态调用方面带来的复杂问题

  • 二义性问题:当通过FinalDerived对象调用baseFunc时,由于存在两条继承路径(Derived1Derived2),编译器不知道应该使用哪个路径的虚函数表中的baseFunc,从而产生二义性。例如,FinalDerived fd; fd.baseFunc();这样的调用会报错。
  • 数据冗余问题:在菱形继承中,Base类的成员在FinalDerived对象中会被重复继承两份(分别来自Derived1Derived2),这不仅浪费内存,还可能导致数据不一致等问题。

3. 解决这些问题的方法

  • 虚继承:在Derived1Derived2继承Base时使用虚继承(class Derived1 : virtual public Baseclass Derived2 : virtual public Base)。这样FinalDerived对象中只会保留一份Base类的成员,避免数据冗余。同时,虚继承也会调整虚函数表结构,使得通过FinalDerived对象调用baseFunc不会产生二义性,编译器能够明确知道应该调用哪个版本的虚函数。
  • 明确指定路径:在调用虚函数时明确指定使用哪个继承路径的虚函数表。例如,fd.Derived1::baseFunc();fd.Derived2::baseFunc(); 这样可以避免二义性,但这种方式不够优雅,且破坏了多态的灵活性。