OOD: Object Oriented Design
1 `理解 C++ 各种 特性` 的 `真正意义`, 才能 `通过 C++ 特性, 说出 你对 软件系统 的 想法`
————————————————————————————————————————————————————
| 意味着
————————————————————————————————————————————————————
[1] public 继承 | is-a 关系
————————————————————————————————————————————————————
[2] virtual 函数 | `接口 必须 被继承`
————————————————————————————————————————————————————
[3] non-virtual 函数 | `接口 和 实现 都必须 被继承`
————————————————————————————————————————————————————
——————————————————————————————————————————————————————————————————————
2 3 种继承 | 意义
——————————————————————————————————————————————————————————————————————
[1] public 继承 | is-a
——————————————————————————————————————————————————————————————————————
[2] private 继承 | is-implemented-in-terms-of + 不允许 进一步派生
| 据某物实现出
——————————————————————————————————————————————————————————————————————
[2] protected 继承 | is-implemented-in-terms-of + 允许 进一步派生
——————————————————————————————————————————————————————————————————————
3 class 间 关系
——————————————————————————————————————————————
[1] is-a
——————————————————————————————————————————————
[2] has-a (有一个) // 7 / 8 节
——————————————————————————————————————————————
[3] is-implemented-in-terms-of // 7 / 8 节
——————————————————————————————————————————————
1 public 继承
要 model 出 is-a
关系
1 `OOP 最重要的 规则
`public 继承` 意味着 `is-a (是一种)` 关系
`LSP: Liskov 替换原则`
Derived `public 继承` Base
则 `需要 Base 对象 ( ptr/ ref ) 的地方, Derived 对象 ( ptr / ref ) 一样可以效劳`
反之不然
原因: `每个 Derived 对象 都是一种/个 Base 对象`
反之不然
`函数 若 期望 arg 为 Base ( ptr/ref ), 也都愿意 接受 Derived 对象 ( ptr/ref )`
2 public 继承 和 is-a 的 `等价关系 并不像 直觉那么简单`
`2 个例子`
(1) `鸟 和 企鹅`
1) `双 class 继承体系`
|
| 语法正确, 但 没 model 出 真实性`
| |\
|/ |
企鹅 is-a 鸟 |
| |
|/ |
鸟会飞 => 企鹅会飞
|
| 我们说 鸟会飞 时,
|/
`真正意思` 是一般 `会飞的鸟`, 但有数 `不会飞的鸟`
2) `3 class 继承体系: model 出 真实性`
鸟: 没声明 fly()
/ \
/ \
/ \
会飞的鸟 企鹅/不会飞的鸟
|
|
|/
声明 fly()
3) `但, 某些 软件系统, 不打算 对飞行 "有所知"`, 则 `无需区分`
会飞和不会飞 的鸟
=> `双 class 继承体系` 更好
4) `编译期/运行期 拒绝` 企鹅 fly` 的 设计: 前者优
——————————————————————————————————————————————————————————————————————————————————————————————
| 实现 | 含义
| |
| 鸟 类 & 企鹅 类 |
——————————————————————————————————————————————————————————————————————————————————————————————
[1] 运行期报错 | 均 声明 fly() 为 vf | 企鹅会飞, 但尝试那么做 是一种错误
| |
| 但 企鹅 类 fly() 中调 `报错函数`|
——————————————————————————————————————————————————————————————————————————————————————————————
[2] 编译期报错 | 均 不声明 fly() | 企鹅不会飞
——————————————————————————————————————————————————————————————————————————————————————————————
(2) 长方形 和 正方形
`适用于 Base 的 事, 却 不适用于 Derived 身上`
| |
| Rectangle | Square
|/ |/
`长 可 独立于 宽 被修改: makeBigger()`
而 `public 继承 主张, 适用于 Base 的事, 一定适用于 Derived`
=> `以 public 继承 model Rectangle 与 Square 的关系 并不正确
compile 会通过, 但 程序 behavior 不对`
2 避免 hide inherited name
1 `内层 scope 会 hide 外围 scope 的 相同 name`
2 引入 `继承`
[1] `Derived 内 hide ( => 看不到 ) Base` 内 `相同 name`
`按 name 查找, 与 para / return type / 是否 virtual 等 无关`
[2] `欲 继承 base 的 同 name 重载函数`
`using 声明`
使 Base 内 `所有` 该 `name 在 Derived 内 可见`
[3] 欲 继承 + `override 部分 同 name 重载函数`
`转交函数 ( forwarding function )`
Derived vf 调用 `Base名 限定 的 Base vf`
virtual void Derived::vf() { Base::vf(); }
3 区分 接口继承
与 实现继承
0 概述
(1) `override ( 覆写 ) = redefine ( 重新定义 )`
——————————————————————————————————————————————————————————————
override 可用
——————————————————————————————————————————————————————————
[1] `different implement`
[2] `same implement` ( as base ): `转调 base 的 缺省实现`
——————————————————————————————————————————————————————————————
(2) public 继承` 下 `pure vf (pvf) / impure vf / non-vf`
public 继承
function `interface/implement 继承`
像 函数 `声明 / 定义` 的 差异
含义: is-a
interface (of mem func) 总是会被 继承
某 mem_func 可用于 Base 上, 一定也能 用于 Derived 上
class 设计者 据不同需求, 可能希望 derived class
————————————————————————————————————————————————————————
1 只继承 interface ( 即 declaration ) of mem func
必须 override implement
`pvf`
————————————————————————————————————————————————————————
2 继承 interface 和 default implement ( 缺省实现 )
希望但 不强迫 override
`impure vf`
————————————————————————————————————————————————————————
3 继承 interface 和 `强制性实现`
|
|/
不允许 override
`non-vf`
————————————————————————————————————————————————————————
1 pvf
(1) 目的
只继承 `interface`
=> 必须 override`
(2) 特点
[1] pvf 使 其所在 class 成为
`abstract (抽象) class`
而 client 不能 new abstract class object
只能 new 相应的 derived class object
[2] `pure vf 可 define`, 但 `唯一 调用方法` 是 `用 class name 限定`
(3) 应用
`为 impure vf 提供` 更 safe 的 `缺省实现`
2 impure vf
(1) 目的
继承 `interface 和 缺省实现`
=> `希望 但 不强制 override`
(2) 典型 OOD: 1种 重构手法
2 class `共同 性质` 提取到 Base, `继承` 以 `避免 code 重复`
|
| 如 `飞行方式`
|/
fly()
(3) 第 3 种 飞机: `飞行方式` 不同
思路
`切断 ( Derived ) vf interface` 与其 `( Base ) 缺省 implement 间` 的 `连接`
——————————————————————————————————————————————————————————
[1] 方法 1
——————————————————————————————————————————————————————
1] Base::pvf 不定义
|
|/
改用 non-vf: define default impl
|
|/
protected
|
|/
给 Derived 用
——————————————————————————————————————————————————————
2] Derived::vf
——————————————————————————————————————————————————
同性质 Derived | override: 调 Base::non-vf
——————————————————————————————————————————————————
独特性质 Derived | override: 新实现
——————————————————————————————————————————————————————————
——————————————————————————————————————————————————————————
[2] 方法 2
——————————————————————————————————————————————————————
1] Base::pvf | define default impl
——————————————————————————————————————————————————————
2] Derived::vf
——————————————————————————————————————————————————
同性质 Derived | override: 转调 Base::pvf
——————————————————————————————————————————————————
独特性质 Derived | override: 新实现
——————————————————————————————————————————————————————————
比较
[1] pvf ( fly ) 分割为 2 部分
`接口` ( 声明部分, Derived 必须使用 )
|
|/
pvf
`缺省实现` ( 定义部分, Derived 明确要求时才使用 )
|
|/
non-vf
好处: 让 `接口 函数` 和 `缺省 实现函数` 享有 不同保护级别 public / protected
[2] 合并 `接口 函数` 和 `缺省 实现函数`
3 non-vf
(1) 目的
继承 `interface 和 强制实现`
|
|/
=> 不允许 override`
(2) 意义
`不变性 ( invariant ) 凌驾 特殊性 ( specialization ) 之上
即 `non-vf 不打算 在 derived class 中 有 不同行为`
derived class 本身 可 特殊化, 但
`non-vf 指定的 行为不可改变`
=> 不该在 derived class 中 override
4 考虑 vf 之外 的 选择
1 `NVI` 手法 实现 `Template Method`
|
|
non-virtual interface
`NVI` ( func ) `wrap 访问性更低` 的 `vf`
| | |
| | |
public private/protected 做实际工作
(1) 优点
wrapper/nvi 可使在 `vf 调用 前/后`
[1] 设定适当场景
lock a mutex / log entry / 验证 class 约束条件
[2] 清理场景
unlock a mutex / 验证 class 约束条件
2 用 `fp` ( function pointer ) 实现 `Strategy` 模式
`non-mem function pointer 作 mem data
|
|/
para 为 class 的 const 引用
(1) `Strategy 模式`
[1] pf (pointer to function) mem
[2] ctor: receive & store 传入的 pf (策略) 到 pf mem
[3] non-vf 用 pf mem 调 func(策略): *this 作 arg
(2) `相对于 A 内 vf 的 优点`
[1] `不同 A object` 可拥有 `不同 策略函数`
[2] `运行期 可改变 策略函数`
(3) 缺点
`class 封装性 降低`
3 泛化
std::bind(&B::f3, b, _1)
|\
| _1: 调用 &B::f3 时, 以 b 作 对象
|
|
[1] `std::function 对象` 可持有 `任何 可调用对象`
只要 `其 签名式 ( signature )` 兼容于 `需求端`
|
|
|/
可调用对象 的 函数调用运算符 returnType `可 隐式转换为 signature 的 returnType`
[2] 与 2 相比
实现上( 仅 1 点 )
普通 `函数指针 类型别名`
|
| 换为
|/
策略 `std::function 类型` 别名
typedef std::function<int (const A&)> PF;
class A; // 前向声明
int defaultPF(const A&);
class A
{
public:
// [1] 策略 std::function 类型 `别名`
typedef std::function<int (const A&)> PF;
// [3]
explicit A(PF pf_ = defaultPF)
: pf(pf_) {}
/ [4]
int nvf() const { return pf(*this); }
private:
PF pf; // [2]
};
// 1) function
short f1(const A&);
// 2) function object
struct F2
{
int operator()(const A&) const { return 1; }
};
// 3) mem func
class B
{
public:
float f3(const A&) const; // mem func
};
class A1: public A // 同2
{
/* .. */
};
// --- client
A1 person1(f1);
A1 person2(F2() );
B b;
A1 person3( std::bind(&B::f3, b, _1) );
4 `委托` 给 `另一 继承体系` 的 `vf`
`经典 Strategy 模式`
与 3 相比
[1] 策略 `std::function 类型` 别名`
|
| 换为
|/
`策略 函数对象`
含 vf
|\
| 调 => `多态 即 多策略`
|
[2] 模板类 Ctor 参数: `策略函数对象 指针`
class A; // 前向声明
class PF // [1]
{
public:
virtual int vf(const A& c) const
{ /* ... */ }
};
PF defaultPF;
class A
{
public:
explicit A(PF* pf_ = &defaultPF) // [3]
: pf(pf_) {}
int nvi() const { return pf->vf(*this); } // [4]
private:
PF* pf; // [2]
};



5 绝不 redefine 继承而来
的 non-virtual func
: 设计上 矛盾
non-vf: non-virtual mem func
1 `业务角度`
non-vf 调用 是
静态绑定
`non-vf 通过 ptr/ref 被调用` 时,
表现的 `行为 取决于` 指向 object 的 `ptr/ref 的 type`
2 `设计角度`
[1] public 继承 意味着 is-a
[2] non-vf 声明
`为 class 建立 不变性/invariant, 凌驾 其 specialization`
=> `若 redefine 继承而来` 的 `non-vf`,
则 上述两者在 `设计上 就 矛盾了`
6 绝不 redefine 继承而来 的 vf 的 默认参数值
1
——————————————————————————————————
[1] ptr/ ref 调 vf 动态绑定
——————————————————————————————————
[2] object 调 vf 静态绑定
——————————————————————————————————
[3] 缺省参数值 静态绑定
——————————————————————————————————
取决于 `ptr/ref 所指 object` 的 `运行期 type`
|\
| 是 Base::vf 还是 Derived::vf
|
(1) `动态绑定 调 vf`
vf `才` 从 base class 继承 `缺省参数`
|
| 是 静态绑定
|
| 用的是 Base::vf 还是 Derived::vf 中的 缺省参数
|/
取决于 ptr / ref 的 type
(2) `override` vf `默认参数值` 的 `问题`
—————————————————————————————————————————————————————————————————
Base ptr
—————————————————————————————————————————————————————————————
[1] 指向 Derived object
[2] 调 vf
Derived::vf / Base::vf 默认参数值 不同
—————————————————————————————————————————————————————————————
=> `override` vf `默认参数值`
=> `vf code 用 Derived 内那份, 但 默认参数值 用 Base 内那份`
即, `Base 和 Derived 的 vf 声明式 各出一半力`
B* pr = new D; // pr 静态类型为 B*
pr->vf(B::Color color = Green); // D::vf(B::Color color = Red )
—————————————————————————————————————————————————————————————————
2 `若 遵守本规则`, `又想提供 缺省参数值` 给 Base 和 Derived 用户
[1] Base
1] `NVI: non-vf 指定 缺省参数`
|
|/
绝不应该被 override
=> 缺省值 总是 Base 中 版本
2] 调 `private pvf 负责 真正工作`
[2] Derived
impure vf override Base::pvf
[3] Base ptr/ref
1] 指向 Derived 对象
2] 调 non-vf ( 继承而来的 )
7 用 composition 建模 has-a ( 有一个 ) / is-implemented-in-terms-of ( 据某物实现出 )
1 `Composition 2 层含义` class A{
| private:
|/ std::string name;
是 `类型 间 关系: 类型1 对象 内含 类型2 对象` B b; class B { /* ... */ };
};
`composition` 发生于
`应用域 / 实现域 object 间`,
表现出 `has-a / is-implemented-in-terms-of` 关系
——————————————————————————————————————————————————————————————
软件中 处理 2个领域 |
——————————————————————————————————————————————————————————————
应用域: 所塑造的世界 中 的事物 | 人 / 汽车
——————————————————————————————————————————————————————————————
实现域: 实现细节 | buffer / mutex / rbtree
——————————————————————————————————————————————————————————————
2 应用: 用 std::list 实现 set
需求: 需 template, 造出 一组 class 表现 `不重复对象 组成的 set`
(1) 方法 1: 用 std::set
[1] `空间 比 速度 重要 时 -> std::set 不合理`
[2] 速度 比 空间 重要 时 -> std::set 合理
(2) 方法 2: public 继承 std::list
template<typename T>
class Set: public std::list<T>{...};
问题
Set 与 std::list 用 is-a 关系 不合适
list 内 元素 可重复
Set 内 元素 不可重复
(3) 方法 3: Set 与 std::list 用 Composition 关系
Set is-implemented-in-terms-of std::list
熟悉 STL, 则 很容易实现
template<typename T>
class Set
{
private:
std::list<T> rep; // 用来 表述 Set 的 data
public:
// ...
};
template<typename T>
void Set<T>::insert(const T& item)
{
if( ! is_member(item) )
rep.push_back(item);
}
8 慎用 private 继承
1 `private 继承` 的 `行为 & 意义`
(1) 行为
private 继承 下
`compiler 不会` 自动 `将 derived class 对象 转换为 base class 对象`
(2) 意义
只在 `软件实现层面 有意义`, 在 `软件设计层面` 没有意义
[1] is-implemented-in-terms-of ( 据 某物 实现出 )`
|
|/
Base
[2] 只有 `implement 部分 被继承, interface 部分 应略去`
2 `private vs. Composition`
意义都是 is-implemented-in-terms-of
(1) 两者 怎么选 ?
尽可能 用 Composition
`必要时` 才用 private 继承
` |
| `何时才必要 ?`
|/
3 种情形
2 个 class (A 和 B) `不存在 is-a 关系`
而是 is-implemented-in-terms-of 关系 时
—————————————————————————————————————————————
[1] A 需 `访问` B 的 `protected 成员`
—————————————————————————————————————————————
[2] A 需 `override` B 的 vf
—————————————————————————————————————————————
[3] `empty base 最优化`
—————————————————————————————————————————————
3 is-implemented-in-terms-of 的 3 种实现
需求
需 `实现类 (定时器 Timer)`
`可按 任何频率 滴答, 每次滴答 调某 vf`
让 `目标类` 可以 override `实现类` 的 vf
|
| 目的
|/
访问 `目标类` 的 data
[1] private 继承
public 继承 不合适
Widget isn't a Timer
[2] `( private 区 ) Composition + public 继承`
1] `成员类` public 继承 `实现类`
| |
| |
|/ |/
WidgetTimer Timer
|
|/
放 private 区
=> `目标类` definition 必须 可见 `实现类` definition
| |
| |
|/ |/
Widget #include "实现类 定义 头文件"
=> 目标类 `依赖` 实现类
2] 据 `成员类` 实现 override `实现类` 的 vf`
——————————————————————————————————————————————————————————————————————————
[2] 与 [1] 相比
——————————————————————————————————————————————————————————————————————
若想 从 目标类 `进一步派生`
又想 `阻止` 派生出的类 `override 成员类型 的 vf`
只能用 [2], 不能用 [1]
——————————————————————————————————————————————————————————————————————————
原因
1] private 继承 ( [1] ) => `阻止了 目标类 进一步派生`
——————————————————————————————————————————————————————————
2] ( private 区 ) Composition + public 继承 ( [2] )
=> `派生出的类` 无法取用 `成员类型`
=> 无法 `override 成员类型 的 vf`
——————————————————————————————————————————————————————————————————————————
[3] `成员类型` 变 `成员(类型)指针`
`成员类`
1] 本身 `移出`
2] 替换为 `其 指针`
`目标类` 只需带 `成员指针 相应类型` 的 declaration
| |
|/ |/
Widget WidgetTimer
——————————————————————————————————————————————————————————————————————
[3] 与 [2] 相比
——————————————————————————————————————————————————————————————————
1] 优势 | 目标类 `编译依赖 降低`
—————————————————————————————————————————————————————————————————— Note
2] 代价 | 目标类 `无法 override 成员(类型)指针 相应类型` 的 vf - - - - -> 只是不符合 需求 中的 override
——————————————————————————————————————————————————————————————————————
4 empty base 最优化
(1) `empty base`
无
non-static 成员变量
vf
virtual base class
往往含
static 成员变量
non-virtual func
typedef
enum
———————————————————————————————————————————————————————
C++ 规定
`empty class`
———————————————————————————————————————————————
[1] `独立 对象` 都 `必须有 非0 大小`
大多 compiler 做法:
Empty class 插 一块足够放 int 的 空间
———————————————————————————————————————————————
[2] 作 成员对象
不占空间
———————————————————————————————————————————————————————
(2) `EBO / empty base optimization: private 继承 Empty class`
`优势: 不增加 Derived class 大小`
[1] EBO 一般只在 单继承 下 可行
[2] STL 中 empty base, 通常内含 typedef
unary_function / binary_function
9 慎用 多重继承
1 多重继承
[1] 问题1
class A public 继承 class B 和 C
`通过 class A 对象 调 B 和 C` 的 `同名 mem func`
二义性
|
| 解决
|/
class B/C name 限定
[2] 问题 2
`菱形继承`
`最远基类 的 mem data 在 最远派生类 中 有 2 份`
|
| 解决
|/
`virtual 继承`
2 virtual 继承
(1) `3 代价` ( 相比于 non-virtual 继承 )
1) class 占用空间 更大
2) 访问 virtual base class 的 成员变量, 访问速度更慢
3) `virtual base class 初始化` 每个 `继承它的 class 都要负责`
(2) 忠告
1) 非必要 不要用 virtual base class
2) 若必须用 virtual base class, 尽量 `避免在其中 放 data`
3 `ImplementClass: public 继承 Interfaceclass + private 继承 ImplementInfoClass`
| |
| 中 |/
| 可替换为 `( private 区 ) Composition + public 继承`
|/
可用 Factory Method `创建 ImplementClass 对象`
// Factory Method
std::shared_ptr<IPerson>
makePerson(DatabaseId personId);
// client
std::shared_ptr<IPerson> spIP( makePerson(id) );
// 辅助函数
DatabaseId id(askUerForDatabaseId() );
DatabaseId askUerForDatabaseId();

=== 详细
1 public 继承
要 model 出 is-a
关系
1 `OOP 最重要的 规则
`public 继承` 意味着 `is-a (是一种)` 关系
`Liskov 替换原则`
Derived `public 继承` Base
则 `需要 Base 对象 ( ptr/ ref ) 的地方, Derived 对象 ( ptr / ref ) 一样可以效劳`
反之不然
原因: `每个 Derived 对象 都是一种/个 Base 对象`
反之不然
`函数 若 期望 arg 为 Base ( ptr/ref ), 也都愿意 接受 Derived 对象 ( ptr/ref )`
// 学生 和 人 的例子
class Person { ... };
class Student: public Person { ... }
void eat(const Person& p);
void study(const Student& s);
2 public 继承 和 is-a 的 `等价关系 并不像 直觉那么简单`
`2 个例子`
(1) `鸟 和 企鹅`
1) `双 class 继承体系`
|
| 语法正确, 但 没 model 出 真实性`
| |\
|/ |
企鹅 is-a 鸟 |
| |
|/ |
鸟会飞 => 企鹅会飞
|
| 我们说 鸟会飞 时,
|/
`真正意思` 是一般 `会飞的鸟`, 但有数 `不会飞的鸟`
class Bird
{
public:
virtual void fly(); // 鸟会飞
};
class Penguin: public Bird
{
// ... // 企鹅会飞: not real
};
2) `3 class 继承体系: model 出 真实性`
鸟: 没声明 fly()
/ \
/ \
/ \
会飞的鸟 企鹅/不会飞的鸟
|
|
|/
声明 fly()
class Bird
{
... // 没声明 fly()
};
class FlyingBird: public Bird
{
public:
virtual void fly();
};
class Penguin: public Bird
{
... // 没声明 fly()
};
3) `但, 某些 软件系统, 不打算 对飞行 "有所知"`, 则 `无需区分`
会飞和不会飞 的鸟
=> `双 class 继承体系` 更好
4) `编译期/运行期 拒绝` 企鹅 fly` 的 设计: 前者优
——————————————————————————————————————————————————————————————————————————————————————————————
| 实现 | 含义
| |
| 鸟 类 & 企鹅 类 |
——————————————————————————————————————————————————————————————————————————————————————————————
[1] 运行期报错 | 均 声明 fly() 为 vf | 企鹅会飞, 但尝试那么做 是一种错误
| |
| 但 企鹅 类 fly() 中调 `报错函数`|
——————————————————————————————————————————————————————————————————————————————————————————————
[2] 编译期报错 | 均 不声明 fly() | 企鹅不会飞
——————————————————————————————————————————————————————————————————————————————————————————————
void error(const std::string& msg);
class Penguin: public Bird
{
virtual void fly() { error("attempt to make a penguin fly"); }
};
Penguin p;
p.fly(); // [1] 运行期 报错
|
|
|/
class Bird
{
// ... // 没声明 fly()
};
class Penguin: public Bird
{
// ... // 没声明 fly()
}
Penguin p;
p.fly(); // [2] 编译期 报错
(2) 长方形 和 正方形
`适用于 Base 的 事, 却 不适用于 Derived 身上`
| |
| Rectangle | Square
|/ |/
`长 可 独立于 宽 被修改: makeBigger()`
而 `public 继承 主张, 适用于 Base 的事, 一定适用于 Derived`
=> `以 public 继承 model Rectangle 与 Square 的关系 并不正确
compile 会通过, 但 程序 behavior 不对`
class Rectangle
{
public:
virtual void setLength(int length_);
virtual void setWidth(int width_);
virtual int length() const;
virtual int width() const;
};
void makeBigger(Rectangle& r)
{
int old_length = r.length();
r.setWidth( r.width() + 10 );
assert( r.length() == old_length );
}
上述 assert 永远为真
class Square: public Rectangle { ... }
Square s;
...
assert(s.length() == s.width() ); // 对正方形 一定为真
makeBigger(s); // s is-a Rectangle
assert(s.length() == s.width() ); // 问题
3 题外话
(1) `并不存在 完美设计` 以 适用于 所有软件
`最佳设计 取决于 系统` 现在和未来 `希望做什么事`
(2) 你的 `设计` 军械库 加上了 `继承` 这门大炮, 你也必须 `为 你的直觉 增加 新的 洞察力`
当某天 有人让你看1个长达数页 的 函数 时, 你终将回忆起
`令 Penguin 继承 Bird, 或 令 Square 继承 Rectangle` 的趣味;
这样的 继承 有可能 接近事实, 也有可能不`
2 避免 hide inherited name





1 `内层 scope 会 hide 外围 scope 的 相同 name`
int x; // global 变量
void someFunc()
{
double x; // local 变量, 为 int 仍 hide
std::cin >> x;
}
2 引入 继承
derived class 内 mem func
refer to base class 内 某物 ( mem func / typedef / mem data ) 时,
compiler 可找出 所 refer to 的 东西
(1) `Derived 内 能看到 Base` 内 `不同 name`
compiler: name 匹配查找过程
——————————————————————————
[1] local scope
[2] 外围 / Derived scope
[3] 再外围 / Base scope
[4] 再 外围 / global scope
——————————————————————————
void Derived::mf4()
{
mf2();
}
(2) `目标 是 避免 name 问题`
而不是 了解 compiler 详细 name 查找规则
`Derived 内 hide ( => 看不到 ) Base` 内 `相同 name`
`按 name 查找, 与 para / return type / 是否 virtual 等 无关`
// Derived 内 hide 了 Base 内 mf1 mf3
Derived d;
int x;
// ...
d.mf1(); // 调 Derived::mf1()
d.mf1(x); // error: Derived::mf1 hide 了 Base::mf1
d.mf3(x); // error: Derived::mf3 hide 了 Base::mf3
(3) `欲 继承 base 的 同 name 重载函数`
`using 声明`
使 Base 内 `所有` 该 `name 在 Derived 内 可见`
d.mf1(x); // 调 Base::mf1
d.mf3(x); // 调 Base::mf3
(4) 欲 继承 + `override 部分 同 name 重载函数`
`转交函数 ( forwarding function )`
3 区分 接口继承
与 实现继承
1 pvf
// 令 pure vf draw 有 定义
Shape* ps = new Rectangle;
ps->draw();
ps->Shape::draw();
2 impure vf
(1) 前2种飞机: `飞行方式` 同
class Airport { ... };
class Airplane
{
public:
virtual void fly(const Airport& dst);
};
void Airplane::fly(const Airport& dst) { /* 缺省 code */ }
class ModelA: public Airplane { /* 可 不声明 fly */ };
class ModelB: public Airplane { /* 可 不声明 fly */ };
(2) 第 3 种 飞机: `飞行方式` 不同
// ====== solution1:
class Airport { /* ... */ };
class Airplane
{
public:
virtual void fly(const Airport& dst) = 0; // 无法 define
protected: // note
void defaultFly(const Airport& dst); // 改用 non-vf ( protected ): define default impl
};
void Airplane::defaultFly(const Airport& dst)
{
// 缺省 行为
}
// --- same 性质: 2 个 class
class ModelA: public Airplane
{
public:
virtual void fly(const Airport& dst) { defaultFly(dst); } // override: 调 Base::non-vf
};
class ModelB: public Airplane
{
public:
virtual void fly(const Airport& dst) { defaultFly(dst); }
};
// --- different 性质: 1 个 class
class ModelC: public Airplane
{
public:
virtual void fly(const Airport& dst);
};
void ModelC::fly(const Airport& dst)
{
// C 的 fly // override: 新实现
}
// ====== solution2:
#include <iostream>
class Airport { /* ... */ };
class Airplane
{
public:
virtual void fly(const Airport& dst) = 0;
};
void Airplane::fly(const Airport& dst) // pvf: define default impl
{
/* 缺省 行为 */
std::cout << "pvf 可定义\n";
}
// --- same 性质: 2 个 class
class ModelA : public Airplane
{
public:
virtual void fly(const Airport& dst) { Airplane::fly(dst); } // override: 转调 Base::pvf
};
class ModelB : public Airplane
{
public:
virtual void fly(const Airport& dst) { Airplane::fly(dst); }
};
// --- different 性质: 1 个 class
class ModelC : public Airplane
{
public:
virtual void fly(const Airport& dst);
};
void ModelC::fly(const Airport& dst)
{
// C 的 fly // override: 新实现
}
int main()
{
Airplane* pb = new ModelB;
Airport a;
pb->fly(a);
}
3 non-vf
4 考虑 vf 之外 的 选择
0 概述
游戏软件 中 `人物 健康值计算`
mem func 声明为 `impure virtual` => 暗示应该有 `缺省实现`
class A
{
public:
virtual int healthValue() const; // dereved class 可 override
};
1 `NVI` 手法 实现 `Template Method`
|
|
non-virtual interface
`NVI` ( func ) `wrap 访问性更低` 的 `vf`
| | |
| | |
public private/protected 做实际工作
(1) 优点
wrapper/nvi 可使在 `vf 调用 前/后`
[1] 设定适当场景
lock a mutex / log entry / 验证 class 约束条件
[2] 清理场景
unlock a mutex / 验证 class 约束条件
class A
{
public:
int nvi() const
{
/* 设定适当场景 */
int ret = vf();
/* 清理场景 */
return ret;
}
private:
virtual int vf() const
{
/* default impl */
}
};
2 用 `fp` ( function pointer ) 实现 `Strategy` 模式
`non-mem function pointer 作 mem data
|
|/
para 为 class 的 const 引用
(1) `Strategy 模式`
[1] pf (pointer to function) mem
[2] ctor: receive & store 传入的 pf (策略) 到 pf mem
[3] non-vf 用 pf mem 调 func(策略): *this 作 arg
class A; // 前向声明
int defaultPF(const A&);
class A
{
public:
// [1] `函数指针 类型别名`
typedef int (*PF)(const A&);
// [3] ctor: receive & store 传入的 pf (策略) 到 pf mem
explicit A(PF pf_ = defaultPf)
: pf(pf_) {}
// [4] non-vf 用 pf mem 调 func(策略): *this 作 arg
int nvf() const
{
return pf(*this);
}
private:
PF pf; // [2] pf (pointer to function) mem
};
class A1: public A
{
public:
explicit A1(PF pf = defaultPf)
: A(pf) { /* ... */ }
};
int PF1(const A&);
int PF2(const A&);
A1 person1(PF1);
A1 person2(PF2);
(2) `相对于 A 内 vf 的 优点`
[1] `不同 A object` 可拥有 `不同 策略函数`
[2] `运行期 可改变 策略函数`
(3) 缺点
`class 封装性 降低`
3 泛化
std::bind(&B::f3, b, _1)
|\
| _1: 调用 &B::f3 时, 以 b 作 对象
|
|
[1] `std::function 对象` 可持有 `任何 可调用对象`
只要 `其 签名式 ( signature )` 兼容于 `需求端`
|
|
|/
可调用对象 的 函数调用运算符 returnType `可 隐式转换为 signature 的 returnType`
[2] 与 2 相比
实现上( 仅 1 点 )
普通 `函数指针 类型别名`
|
| 换为
|/
策略 `std::function 类型` 别名
typedef std::function<int (const A&)> PF;
class A; // 前向声明
int defaultPF(const A&);
class A
{
public:
// [1] 策略 std::function 类型 `别名`
typedef std::function<int (const A&)> PF;
// [3]
explicit A(PF pf_ = defaultPF)
: pf(pf_) {}
/ [4]
int nvf() const { return pf(*this); }
private:
PF pf; // [2]
};
// 1) function
short f1(const A&);
// 2) function object
struct F2
{
int operator()(const A&) const { return 1; }
};
// 3) mem func
class B
{
public:
float f3(const A&) const; // mem func
};
class A1: public A // 同2
{
/* .. */
};
// --- client
A1 person1(f1);
A1 person2(F2() );
B b;
A1 person3( std::bind(&B::f3, b, _1) );
4 `委托` 给 `另一 继承体系` 的 `vf`
`经典 Strategy 模式`
与 3 相比
[1] 策略 `std::function 类型` 别名`
|
| 换为
|/
`策略 函数对象`
含 vf
|\
| 调 => `多态 即 多策略`
|
[2] 模板类 Ctor 参数: `策略函数对象 指针`
class A; // 前向声明
class PF // [1]
{
public:
virtual int vf(const A& c) const
{ /* ... */ }
};
PF defaultPF;
class A
{
public:
explicit A(PF* pf_ = &defaultPF) // [3]
: pf(pf_) {}
int nvi() const { return pf->vf(*this); } // [4]
private:
PF* pf; // [2]
};
5 绝不 redefine 继承而来
的 non-virtual func
: 设计上 矛盾
class Base
{
public:
void nvf();
};
class Derived: public Base
{
public:
void nvf(); // hide 了 Base::nvf
};
Derived d;
Base* pb = &d;
Derived* pd = &d;
pb->nvf(); // 调 Base::nvf
pd->nvf(); // 调 Derived::nvf
6 绝不 redefine 继承而来 的 vf 的 默认参数值
1
——————————————————————————————————
[1] ptr/ ref 调 vf 动态绑定
——————————————————————————————————
[2] object 调 vf 静态绑定
——————————————————————————————————
[3] 缺省参数值 静态绑定
——————————————————————————————————
取决于 `ptr/ref 所指 object` 的 `运行期 type`
|\
| 是 Base::vf 还是 Derived::vf
|
(1) `动态绑定 调 vf`
vf `才` 从 base class 继承 `缺省参数`
|
| 是 静态绑定
|
| 用的是 Base::vf 还是 Derived::vf 中的 缺省参数
|/
取决于 ptr / ref 的 type
(2) `override` vf `默认参数值` 的 `问题`
—————————————————————————————————————————————————————————————————
Base ptr
—————————————————————————————————————————————————————————————
[1] 指向 Derived object
[2] 调 vf
Derived::vf / Base::vf 默认参数值 不同
—————————————————————————————————————————————————————————————
=> `override` vf `默认参数值`
=> `vf code 用 Derived 内那份, 但 默认参数值 用 Base 内那份`
即, `Base 和 Derived 的 vf 声明式 各出一半力`
B* pr = new D; // pr 静态类型为 B*
pr->vf(B::Color color = Green); // D::vf(B::Color color = Red )
—————————————————————————————————————————————————————————————————
class B
{
public:
enum Color {Red, Green};
virtual void vf(Color color = Red) const = 0;
};
class D: public B
{
public:
virtual void vf(Color color = Green) const;
};
B* ps; // ps 静态类型为 B*
B* pr = new D; // pr 静态类型为 B*
ps = pr; // ps 动态类型 变为 D*
pr->vf(); // D::vf(B::Red)
2 `若 遵守本规则`, `又想提供 缺省参数值` 给 Base 和 Derived 用户
[1] Base
1] `NVI: non-vf 指定 缺省参数`
|
|/
绝不应该被 override
=> 缺省值 总是 Base 中 版本
2] 调 `private pvf 负责 真正工作`
[2] Derived
impure vf override Base::pvf
[3] Base ptr/ref
1] 指向 Derived 对象
2] 调 non-vf ( 继承而来的 )
class B
{
public:
enum Color {Red, Green};
// 1 NVF/non-vf
void nonVf(Color color = Red) const { doVf(color); }
private:
virtual void doVf(Color color) const = 0; // 2 pure vf
};
class D: public Color
{
private:
virtual void doVf(Color color) const; // 3 impure vf
};
7 用 composition 建模 has-a ( 有一个 ) / is-implemented-in-terms-of ( 据某物实现出 )
1 `Composition 2 层含义` class A{
| private:
|/ std::string name;
是 `类型 间 关系: 类型1 对象 内含 类型2 对象` B b; class B { /* ... */ };
};
`composition` 发生于
`应用域 / 实现域 object 间`,
表现出 `has-a / is-implemented-in-terms-of` 关系
——————————————————————————————————————————————————————————————
软件中 处理 2个领域 |
——————————————————————————————————————————————————————————————
应用域: 所塑造的世界 中 的事物 | 人 / 汽车
——————————————————————————————————————————————————————————————
实现域: 实现细节 | buffer / mutex / rbtree
——————————————————————————————————————————————————————————————
2 应用: 用 std::list 实现 set
需求: 需 template, 造出 一组 class 表现 `不重复对象 组成的 set`
(1) 方法 1: 用 std::set
1) `空间 比 速度 重要 时 -> std::set 不合理`
2) 速度 比 空间 重要 时 -> std::set 合理
(2) 方法 2: public 继承 std::list
template<typename T>
class Set: public std::list<T>{...};
问题
Set 与 std::list 用 is-a 关系 不合适
list 内 元素 可重复
Set 内 元素 不可重复
(3) 方法 3: Set 与 std::list 用 Composition 关系
Set is-implemented-in-terms-of std::list
熟悉 STL, 则 很容易实现
template<typename T>
class Set
{
private:
std::list<T> rep; // 用来 表述 Set 的 data
public:
bool is_member(const T& item) const;
void insert(const T& item);
void remove(const T& item);
std::size_t size() const;
};
template<typename T>
bool Set<T>::is_member(const T& item) const
{
// != 为 true => find -> is member
return std::find( rep.begin(), rep.end(), item) != rep.end();
}
template<typename T>
void Set<T>::insert(const T& item)
{
if( ! is_member(item) )
rep.push_back(item);
}
template<typename T>
void Set<T>::remove(const T& item)
{
typename std::list<T>::iterator iter
= std::find(rep.begin(), rep.end(), item);
if(it != rep.end() )
rep.erase(iter);
}
template<typename T>
std::size_t Set<T>::size() const
{
return rep.size();
}
8 慎用 private 继承
1 `private 继承` 的 `行为 & 意义`
(1) 行为
private 继承 下
`compiler 不会` 自动 `将 derived class 对象 转换为 base class 对象`
class Person { ... };
class Student: private Person{ ... };
void eat(const Person&);
void study(const Student&);
Student s;
eat(s); // error
(2) 意义
只在 `软件实现层面 有意义`, 在 `软件设计层面` 没有意义
[1] is-implemented-in-terms-of ( 据 某物 实现出 )`
|
|/
Base
[2] 只有 `implement 部分 被继承, interface 部分 应略去`
2 `private vs. Composition`
意义都是 is-implemented-in-terms-of
(1) 两者 怎么选 ?
尽可能 用 Composition
`必要时` 才用 private 继承
` |
| `何时才必要 ?`
|/
3 种情形
2 个 class (A 和 B) `不存在 is-a 关系`
而是 is-implemented-in-terms-of 关系 时
—————————————————————————————————————————————
[1] A 需 `访问` B 的 `protected 成员`
—————————————————————————————————————————————
[2] A 需 `override` B 的 vf
—————————————————————————————————————————————
[3] `empty base 最优化`
—————————————————————————————————————————————
3 is-implemented-in-terms-of 的 3 种实现
需求
需 `实现类 (定时器 Timer)`
`可按 任何频率 滴答, 每次滴答 调某 vf`
让 `目标类` 可以 override `实现类` 的 vf
|
| 目的
|/
访问 `目标类` 的 data
[1] private 继承
public 继承 不合适
Widget isn't a Timer
class Timer
{
public:
explicit Timer(int tickFreq);
virtual void onTick() const; // 定时器 每滴答 1 次, 该函数 被 自动调用 1 次
};
class Widget: private Timer
{
private:
virtual void onTick() const; // 访问 Widget 的 data
};
[2] `( private 区 ) Composition + public 继承`
1] `成员类` public 继承 `实现类`
| |
| |
|/ |/
WidgetTimer Timer
|
|/
放 private 区
=> `目标类` definition 必须 可见 `实现类` definition
| |
| |
|/ |/
Widget #include "实现类 定义 头文件"
=> 目标类 `依赖` 实现类
2] 据 `成员类` 实现 override `实现类` 的 vf`
——————————————————————————————————————————————————————————————————————————
[2] 与 [1] 相比
——————————————————————————————————————————————————————————————————————
若想 从 目标类 `进一步派生`
又想 `阻止` 派生出的类 `override 成员类型 的 vf`
只能用 [2], 不能用 [1]
——————————————————————————————————————————————————————————————————————————
原因
1] private 继承 ( [1] ) => `阻止了 目标类 进一步派生`
——————————————————————————————————————————————————————————
2] ( private 区 ) Composition + public 继承 ( [2] )
=> `派生出的类` 无法取用 `成员类型`
=> 无法 `override 成员类型 的 vf`
——————————————————————————————————————————————————————————————————————————
// 1 widget.h
class Widget
{
private:
class WidgetTimer: public Timer
{
public:
virtual void onTick() const;
};
WidgetTimer timer;
};
[3] `成员类型` 变 `成员(类型)指针`
`成员类`
1] 本身 `移出`
2] 替换为 `其 指针`
`目标类` 只需带 `成员指针 相应类型` 的 declaration
| |
|/ |/
Widget WidgetTimer
——————————————————————————————————————————————————————————————————————
[3] 与 [2] 相比
——————————————————————————————————————————————————————————————————
1] 优势 | 目标类 `编译依赖 降低`
——————————————————————————————————————————————————————————————————
2] 代价 | 目标类 `无法 override 成员(类型)指针 相应类型` 的 vf => 只是不符合 需求 中的 override
——————————————————————————————————————————————————————————————————————
// 1 widgetTimerDecl.h
class WidgetTimer;
// 2 widget.h
#include "widgetTimerDecl.h"
class Widget
{
private:
WidgetTimer* ptimer;
};
// 3 widgetTimer.h
class WidgetTimer: public Timer
{
public:
virtual void onTick() const;
};
4 empty base 最优化
(1) `empty base`
无
non-static 成员变量
vf
virtual base class
往往含
static 成员变量
non-virtual func
typedef
enum
———————————————————————————————————————————————————————
C++ 规定
`empty class`
———————————————————————————————————————————————
[1] `独立 对象` 都 `必须有 非0 大小`
大多 compiler 做法:
Empty class 插 一块足够放 int 的 空间
———————————————————————————————————————————————
[2] 作 成员对象
不占空间
———————————————————————————————————————————————————————
// sizeof(Empty) > 0
class Empty {};
// sizeof(HoldsAnInt) > sizeof(int)
class HoldsAnInt
{
private:
int x;
Empty e;
};
(2) `EBO / empty base optimization: private 继承 Empty class`
`优势: 不增加 Derived class 大小`
[1] EBO 一般只在 单继承 下 可行
[2] STL 中 empty base, 通常内含 typedef
unary_function / binary_function
// sizeof(Empty) > 0
class Empty {};
// sizeof(HoldsAnInt) == sizeof(int)
class HoldsAnInt: private Empty
{
private:
int x;
};
网友评论