MST

星途 面试题库

面试题:C++中多继承与虚继承的内存布局差异

假设有类A、类B,类C继承自类A和类B,分别阐述在普通多继承和虚继承情况下,类C的对象的内存布局是怎样的,同时说明虚继承解决了什么问题,为什么会出现这种内存布局的差异。
21.8万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

普通多继承情况下类C对象的内存布局

  1. 布局结构
    • 类C对象的内存布局通常是先存放从类A继承来的数据成员,然后接着存放从类B继承来的数据成员。如果类C有自己的数据成员,那么在类A和类B的数据成员之后存放。
    • 例如,如果类A有数据成员a,类B有数据成员b,类C有数据成员c,那么类C对象的内存布局可能是abc的顺序(具体顺序可能因编译器实现而有所不同)。
  2. 示例代码(C++)
class A {
public:
    int a;
};
class B {
public:
    int b;
};
class C : public A, public B {
public:
    int c;
};

在这种情况下,sizeof(C)的值通常是sizeof(A) + sizeof(B) + sizeof(c)(可能会考虑内存对齐等因素)。

虚继承情况下类C对象的内存布局

  1. 布局结构
    • 类C对象的内存布局会更加复杂。通常在对象开头会有一个指向虚基类表(vbtable)的指针(vptr),虚基类表中存储了偏移量等信息,用于找到虚继承的基类(这里是类A和类B的共同基类,如果存在的话)的数据成员位置。
    • 类C自己的数据成员在内存中紧跟在这个指针之后,然后再存放通过虚继承从类A和类B继承来的数据成员(如果有的话)。
  2. 示例代码(C++)
class A {
public:
    int a;
};
class B {
public:
    int b;
};
class C : virtual public A, virtual public B {
public:
    int c;
};

在这种情况下,sizeof(C)的值除了包含abc的大小,还会包含vptr等额外开销。

虚继承解决的问题

  1. 菱形继承问题
    • 虚继承主要解决菱形继承带来的“数据冗余”和“二义性”问题。例如,假设有类D继承自类C,类C虚继承自类A和类B,类A和类B又都继承自同一个基类Base。在普通多继承下,类D中会存在Base类数据成员的两份拷贝,这就造成了数据冗余。同时,如果通过类D访问Base类的成员,会出现二义性,因为编译器不知道应该访问从类A继承来的Base类成员还是从类B继承来的Base类成员。
    • 虚继承通过让所有虚继承的类共享一份基类数据成员,解决了数据冗余问题。并且由于只有一份基类数据成员,也解决了访问时的二义性问题。

内存布局差异的原因

  1. 普通多继承
    • 普通多继承按照简单的线性方式布局内存,每个基类的数据成员依次排列,这种方式简单直接,易于理解和实现,但会导致菱形继承时的数据冗余和二义性问题。
  2. 虚继承
    • 虚继承为了实现共享基类数据成员,需要额外的机制来定位虚基类的数据成员。通过引入虚基类表和vptr指针,能够在运行时准确找到虚基类数据成员的位置,虽然增加了内存开销和复杂性,但有效解决了菱形继承的问题。