《Effective C++》条款 34:区分接口继承与实现继承
公有继承的本质
公有继承(public inheritance)实质上是两种继承的复合体:函数接口的继承和函数实现的继承。而如何混合搭配这两种继承,就体现在你为基类成员函数所选择的类型上。
三种成员函数的对比
| 函数类型 | 继承内容 | 设计意图与约束 |
|---|---|---|
| 纯虚函数 (Pure Virtual) | 仅继承接口 | 强制派生类必须实现该接口。强调"做什么"由基类规定,但"如何做"完全由派生类决定。 |
| 虚函数 (Impure Virtual) | 继承接口和一份缺省实现 | 派生类应该实现该接口,但如果不实现,则使用基类提供的缺省行为。这是灵活性,也潜藏风险。 |
| 非虚函数 (Non-Virtual) | 继承接口和一份强制性实现 | 派生类不应该改变该函数的行为。它代表一种不变性,强调"如何做"是固定的,所有派生类必须遵守。 |
深入理解三种函数的设计意图
1. 纯虚函数:强制实现契约
纯虚函数通过 = 0 的语法,明确告诉派生类的设计者:"你必须支持这个操作,但我无法提供通用实现"。这最适合定义一种"契约"或"能力"。
代码示例:
class Shape {
public:
virtual void draw() const = 0; // 纯虚函数:所有图形都必须能被绘制
virtual ~Shape() = default;
};
Shape::draw() 是纯虚函数,因为画一个圆形和画一个矩形的算法完全不同,基类 Shape 无法给出有意义的默认实现。任何从 Shape 派生的具体类(如 Circle,Rectangle),如果不实现 draw(),自己也会成为抽象类,无法实例化。这就从语法层面保证了接口的一致性。
一个重要的技巧:纯虚函数可以有实现!
你可以为纯虚函数提供定义,但调用它必须使用完全限定名(如 shapePtr->Shape::draw())。这通常用于提供一些公共基础功能,但即便如此,派生类也必须重写该函数。
2. 虚函数:提供缺省实现与风险
虚函数则在强制接口之外,提供了一份"保底"的缺省实现。它说:"你最好自定义这个行为,但如果你不自定义,可以用我这个默认的"。
代码示例:
// 初始设计:存在风险
class Airplane {
public:
{
}
};
: Airplane { ... };
: Airplane { ... };


