单继承时基类成员在前、派生类成员在后;多重继承按声明顺序线性拼接各基类成员;虚继承通过vbptr共享唯一虚基类实例,避免冗余与二义性。

继承关系中的内存分配,核心是派生类对象在内存中如何组织基类部分和自身新增部分。它不是凭空想象的逻辑结构,而是编译器实际写入内存的字节排布,直接影响指针转换、sizeof结果、成员访问效率,甚至虚函数调用。
单继承:基类在前,派生类成员追加在后
这是最直观的布局方式。编译器把基类的全部非静态数据成员按声明顺序放在对象起始位置,再把派生类自己新增的成员变量依次排在其后。
- 例如:
class A { int a; char b; };占 8 字节(含对齐);class B : public A { int c; };就是a(4字节)→b(1字节)→ 填充 3 字节 →c(4字节),共 12 字节 - 好处是:
B*转成A*无需偏移计算——两者地址相同,直接复用指针值 - 所有成员函数(包括虚函数)仍存于代码区,不占对象空间;只有虚函数表指针(
vfptr)会插入对象头部(如有虚函数)
多重继承:按继承声明顺序依次拼接
当一个类同时继承多个基类时,内存中不是“堆叠”,而是**线性拼接**:第一个基类成员排最前,第二个基类成员紧随其后,依此类推,最后才是派生类自己的成员。
- 如:
class D : public A, public B, public C { int d; };内存布局就是A的成员 →B的成员 →C的成员 →d - 此时
D*转成A*地址不变,但转成B*就需要加上sizeof(A)的偏移量,C*更要加前两个基类大小之和 - 这种偏移由编译器在编译期固化,不是运行时计算
虚继承:解决菱形继承的数据冗余与二义性
当多个路径继承同一个基类(如 B : virtual A,C : virtual A,D : B, C),普通继承会让 D 中出现两份 A 的副本,造成浪费和访问歧义。虚继承强制只保留一份 A 实例,并通过**虚基类表指针(vbptr)** 动态定位它。
- 虚继承对象中会多出至少一个指针(vbptr),指向一张记录各虚基类偏移的表
-
A的数据不再固定在开头或某处,而是在整个对象末尾(常见实现),靠 vbptr 查表才能找到其真实地址 - 这带来轻微运行时开销,但换来唯一性和语义正确性
成员遮蔽与同名变量:各自独立占用空间
如果派生类定义了与基类同名的成员变量(如 int m_b; 遮蔽了基类的 m_b),它们在内存中是**两份独立存储**,互不覆盖。
- 编译器不会合并或跳过,而是照常分配空间:基类的
m_b在基类区域,派生类的m_b在派生区域 - 访问时靠作用域限定符区分:
this->m_b指当前类的,Base::m_b指基类的 - 这说明:名字相同 ≠ 同一内存位置;C++ 的“遮蔽”是作用域规则,不是内存覆盖

















