MST

星途 面试题库

面试题:C++多重继承与菱形继承的深度剖析

在C++中,考虑一个多重继承的场景,例如`class A`,`class B`继承自`class Base`,然后`class C`继承自`class A`和`class B`(菱形继承结构)。请分析这种菱形继承结构会带来哪些问题,如何解决这些问题(如使用虚继承),并深入探讨虚继承在内存布局和实现机制上与普通继承的差异。
15.9万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

菱形继承带来的问题

  1. 数据冗余:因为CAB间接继承了Base的成员,导致Base中的数据成员在C对象中存在两份,浪费内存空间。
  2. 歧义性:当C调用Base中的成员函数或访问成员变量时,会产生歧义,编译器不知道应该调用从A继承过来的版本还是从B继承过来的版本。

解决方法 - 虚继承

通过使用虚继承可以解决菱形继承带来的问题。在AB继承Base时使用虚继承,即class A : virtual public Baseclass B : virtual public Base 。这样,无论C从多少条路径继承BaseBaseC对象中都只有一份实例。

虚继承与普通继承在内存布局和实现机制上的差异

  1. 内存布局
    • 普通继承:在普通继承中,派生类对象内存布局是按照继承关系依次排列基类成员和派生类新增成员。对于菱形继承,如果不使用虚继承,Base成员会在C对象中出现多次。
    • 虚继承:虚继承下,派生类对象中会有一个虚基表指针(vptr),它指向一个虚基表(vbtable)。虚基表中记录了虚基类相对于派生类对象起始地址的偏移量。通过这种方式,即使从多个路径继承虚基类,也能保证虚基类只有一份实例,节省内存空间。
  2. 实现机制
    • 普通继承:普通继承关系简单直接,编译器在编译时就能确定成员的访问偏移地址。
    • 虚继承:虚继承需要额外的运行时支持。当访问虚基类成员时,程序需要通过虚基表指针找到虚基表,再从虚基表中获取虚基类成员的偏移量,进而访问到正确的成员。这增加了访问的开销,但解决了菱形继承的数据冗余和歧义问题。