多层继承是单链式(a→b→c),析构需虚析构函数防泄漏,父类指针仅能访问自身成员;多重继承是多源式(c:a,b),易引发二义性、内存偏移和菱形继承问题,须用作用域限定或虚继承解决。

多层继承是“一条线”,多重继承是“多个源头”——结构不同,指针转换、二义性和内存布局的处理方式就完全不同。
多层继承:A → B → C,调用链和析构顺序必须手动保障
这是单链式派生,C 间接继承 A 的所有可访问成员。关键点不在“能不能用”,而在“怎么安全地用”:
- 构造顺序固定为
A→B→C,析构顺序相反;若顶层基类A的析构函数不是virtual,用A*指针 deleteC对象会导致B和C的析构函数不执行,直接内存泄漏 -
A*指针只能调用A中声明的成员(含虚函数),哪怕C重写了它;想调用C特有函数,必须先dynamic_cast<c>(ptrA)</c>并检查是否非空 - 继承方式影响成员可见性:比如
B以protected继承A,则C就无法在外部通过C对象访问A::public成员
多重继承:C : public A, public B,二义性是第一道门槛
当两个基类都定义了同名成员(如 func() 或 data),而派生类没覆盖时,c.func() 编译直接报错:request for member ‘func’ is ambiguous。这不是语法错误,是设计信号:
- 必须显式指定作用域,如
c.A::func()或c.B::func() - 若想统一行为,应在
C中重写该函数,否则无法通过编译 - 每个基类子对象在
C实例中占据独立内存区域,sizeof(C)通常大于sizeof(A) + sizeof(B)(因对齐、vptr 等开销) -
A*和B*都能合法指向同一个C对象,但它们的地址值可能不同(因基类子对象起始偏移不同),不能简单 reinterpret_cast
菱形继承不是“第三种继承”,而是多重+多层混合后的典型陷阱
当 D 同时继承 B 和 C,而 B、C 又都继承自 A,D 会包含两份 A 的副本。这时 D 对象里有两个 A::data,调用 A::func() 也会二义。解决它必须用 virtual 继承:
- 写成
class B : virtual public A和class C : virtual public A,才能让D中只保留一份A子对象 - 虚继承带来额外开销:每个虚基类引入一个虚基类表指针(vbptr),且构造顺序更复杂(虚基类
A由最派生类D直接构造) - 虚继承后,
A*指针转D*必须用dynamic_cast,static_cast不可靠
真正容易被忽略的是:多层继承出问题常在运行时(析构漏掉、转型失败),而多重继承出问题大多卡在编译期(二义性、指针偏移)。别等程序崩了才回头查继承树——画个草图,标清楚哪些函数被重写、哪些析构是 virtual、哪些继承加了 virtual,比写十行代码还管用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











