继承
前面已经说明了两种类间关系组合和委托,现在,我们要学习第三种也是面向对象编程中标志性的一种关系:继承。
public继承的语义
定义: public继承代表一种逻辑上的“是一种”(is-a)关系。当类 Student 使用 public 方式继承类 Person 时,即断言:任何一个 Student 对象,同时也是一个 Person 对象。
语法:
1 | class Student : public Person { ... }; |
注意:只有 public 继承才表达“is-a”关系。private 和 protected 继承通常表达“根据某物实现出”,是组合的一种变体,而非真正的逻辑继承。
常见误解:继承只是为了复用代码,子类能调用父类函数即可。
正确认知:看到 class A : public B,必须能逻辑上断言“A是一种B”(如 Student 是一种 Person)。若逻辑不成立(如 Pencil : public Eraser),即使编译通过,设计也是错误的。继承首先是设计语言,其次才是复用工具。
子类对象的内存结构
核心概念:父类成分,在内存中,一个子类对象的内部,完整地包含了一个父类对象的所有数据成员,这部分称为“父类成分”。

布局顺序:父类成分在低地址,子类新增成员紧接其后。父类指针 Base* p = new Derived() 指向子类对象的起始地址。
继承下的构造与析构顺序
构造顺序(由内而外): 先调用父类构造函数初始化父类成分,再调用子类构造函数初始化子类新增部分。如同“先打地基,再盖房子”。
析构顺序(由外而内): 先调用子类析构函数清理子类特有资源,再自动调用父类析构函数清理父类资源。如同“先拆房顶,再拆地基”。
1 | class Base { |
析构函数必须为 virtual
核心规则: 如果类被设计为父类(即将被继承),其析构函数必须声明为 virtual。否则,通过父类指针删除子类对象时,将导致未定义行为(子类部分不会被正确析构)。
经验法则:只要类中有一个虚函数,析构函数就必须是 virtual。即使类中没有虚函数,但预期会被继承,也建议将析构函数声明为 virtual,以防范未来扩展。
1 | class Base { |





