派生类与基类是“is-a”继承关系,内存中基类子对象位于派生类对象起始处,构造先基类后派生类,析构顺序相反;公有继承承诺接口契约,支持Liskov替换;虚函数实现动态绑定,私有成员不可直接访问。

派生类和基类是“is-a”关系,不是组合或依赖,而是类型层级上的继承关系:一个 RatedPlayer 就是一个 TableTennisPlayer,就像“猫是一种动物”——这种关系决定了内存布局、调用顺序和类型转换是否合法。
派生类对象内存里真包含一个基类子对象
编译器把派生类对象看作“基类部分 + 派生类新增部分”的连续内存块。基类成员总在前面,派生类成员紧随其后。所以 Base* 指针能直接指向 Derived 对象首地址,无需偏移计算。
- 这意味着
static_cast<base>(derived_obj)是安全的,且开销为零 - 但反过来,
static_cast<derived>(base_ref)</derived>是未定义行为,除非你 100% 确定它原本就是Derived实例 - 多重继承时,若存在虚继承,基类子对象可能不位于起始地址,此时指针转换需编译器插入调整值
构造/析构顺序固定且不可绕过
创建 RatedPlayer 对象时,C++ 强制先调用 TableTennisPlayer 的构造函数(哪怕你没写初始化列表),再执行派生类自己的构造体;析构则完全反过来。
- 如果你在派生类构造函数体内尝试访问基类私有成员(比如直接赋值
firstname = "xxx"),编译会报错——它根本不可见 - 基类构造失败(抛异常)会导致派生类构造中止,已构造的基类部分自动析构
- 基类析构函数没声明
virtual,而你用Base*删除Derived对象,将只调用Base::~Base(),派生类资源泄漏
公有继承下,基类接口对用户完全可见
public 继承不是“复用代码”的快捷方式,而是对外承诺接口契约:所有能对 Base 做的操作,也应能对 Derived 做(Liskov 替换原则)。
立即学习“C++免费学习笔记(深入)”;
-
RatedPlayer对象可传给任何接受const TableTennisPlayer&的函数 - 如果基类有虚函数,派生类重写后,通过基类指针调用的就是派生类版本
- 但若你用
private或protected继承,这种隐式转换就失效了——编译器禁止Base*指向它
最常被忽略的一点:派生类无法“修改”基类私有成员的访问路径,哪怕它自己定义了同名变量,那也只是隐藏而非覆盖;所有对基类状态的变更,必须经由基类提供的 public/protected 接口,比如 ResetTable() 而不是直接写 hasTable = true。


















