第16章 继承:共享布局,而不是共享分派

English

1. 本章是什么,以及不是什么

C++ 的继承是一个特性,两半。布局与访问那一半:派生类在最前面含有一个 基类子对象,继承它的成员,可以在任何需要基类的地方使用。另一半是通过 virtual 函数实现的动态分派。

本章只讲第一半。这里每个例子都在编译期被编译器解析完;没有一个需要 vtable,而 virtual 这个词只在一页里出现 —— 第16.7章 —— 因为 override 这个关键字没有它就毫无意义。分派那一章是第18章。

分开讲的理由是:两半的用途和代价都不同。继承布局是免费的、常见的、 自身就有用的:labeled_point 确实就是一个带名字的 point。 继承接口则是每个对象一个指针、每次操作一次间接调用,而且它就是 第15.10章类型擦除的替代方案。 把这两件事混在一起谈,正是人们最后以为“继承就是多态”的原因。

2. 每个想法的一句话总结

想法 一句话 章节
基类子对象 派生对象把自己基类对象含在自己里面,且排在最前面。 第16.1章
构造顺序 先基类,再成员,最后函数体;析构顺序完全相反。 第16.2章
访问说明符 public 继承意味着 is-a;private 继承接管一个实现, 并用 using 公开选中的操作。 第16.3章
派生类的五法则 五个成员每一个都要处理基类子对象 —— 而赋值时忘掉基类也能编译通过。 第16.4章
转换与截断 派生对象就是一个基类对象,所以能绑到基类引用上 —— 也能被拷贝进基类值里,从而丢掉其余部分。 第16.5章
名字隐藏 派生类里声明的名字会隐藏基类同名的一切重载,除非你写 using。 第16.6章
override 一个注解,让编译器去证明这个派生函数确实覆盖了基类函数。 第16.7章
组合 只要 is-a 不是字面为真,成员就比基类更好。 第16.8章

3. 贯穿全章的示例

一个小小的层次结构会走完本章:一台由基类部件与发动机组成的机器。


class machine_part
{
protected:
	::std::int_least32_t serial_{};

public:
	::std::int_least32_t serial() const noexcept { return serial_; }
};

class machine : public machine_part
{
protected:
	::std::int_least32_t power_{};

public:
	::std::int_least32_t power() const noexcept { return power_; }
};

machine is-a machine_part:每台机器都有序列号, 而任何机器都能绑定到 machine_part& 上。这里没有任何东西是虚的, 一切都在编译期决定。

4. 本章约定

5. 你应该已经会的东西

6. 目录