catalog
- 39. use private inheritance judiciously
- 38. model "has-a" or "is-implemented-in-terms-of" through composition
- 37. never redefine a function's inherited default parameter value
- 36. never redefine an inherited non-virtual function
- 32. make sure public inheritance models `is-a`
- 35. consider alternatives to virtual functions
- view c++ as a federation of languages
- prefer const/enum/inline to #define
- use const whenever possible
- make sure that objects are initialized before they're used
- explicitly disallow the use of compiler-generated functions you don't want
- declare destructors virtual in polymorphic class
- never call virtual function during construction or deconstruction
- 12. copy all parts of an object
- Resource Management
- Declare member-data private
- Prefer "non-member non-friend functions" to "member function"
- custom `swap` function
- postpone variable definitons
- Minimize casting
- Minimize compilation dependencies between files
39. use private inheritance judiciously
回想 (public时, Student : public Person
, 即, 学生 是 一种 人) 这个例子;
而且, 编译器, 在必要时, 会自动的, 将Student 强转为 Person;
但是, 如果是: private继承
, 编译器不会自动强转, 而且我们思考下
Person有一个public void eat()
, 而如果是B : private继承 Person
, 这个B里, 是没有eat
的
即B不具备: eat功能, 也说明: B 不是 Person人;
B : private继承 A
纯粹是一种 (实现技术), 他在 (观念 或 逻辑意义上) 没有实际意义
仅仅表示: B 根据A 实现出来
, 即implement in terms of...
(根据…实现)
而我们知道, (38 条款中), 复合也是有implement in tems of
意义, 两者如何选择呢?
优先使用 (复合), 必要时才使用(private继承)
何为(必要时)呢? 当涉及到(virtual虚函数
时, 或 protected数据
时), 使用private继承
class Timer{
public:
virtual void time_out() = 0;
}
比如, 我们有一个Widget
类, 需要使用Timer计时器功能, (只需重写time_out()
即可)
private继承
class Widget : private Timer{
private:
virtual void time_out(){
...}
}
正确, 但这样设计不好, 最好的设计是 (复合)
复合
class Widget{
private:
class MyTimer : public TImer{
public:
virtual void time_out(){
...}
};
MyTimer timer;
}
这种方式 (public继承 + 复合), 是更好的
38. model “has-a” or “is-implemented-in-terms-of” through composition
in-terms-of: 通过
通过 (复合) 来塑造 (“是一种” 或 “由…组成”)
所谓 composition: 复合
, 很简单, 就是指 (成员变量); 只是, 这个 (成员变量), 所表现出的 (含义): 有两种
has-a
class Address{
};
class Person{
Address addr;
}
Person中有一个addr
, 这addr, 就称为: Person中, (复合)了一个 addr
这里的(复合), 塑造的是一种 (has-a): 即Person 有一个 addr
is-implemented
template <class _T>
class My_Set{
public:
void insert( _T _t);
bool contains( _T _t);
void remove( _T _t);
private:
::std::vector< _T> vec;
}
比如说, 我们需要一个set集合
, (但是, 又不想使用std::set
, 因为: std::set为了追求极度的时间, 而带来的多余的空间消耗)
我们先自己写一个set, 他虽然是O(n)
的时间, 但是, 他的空间 要比std::set
好;
此时, 这个 (成员变量vec), 他是 通过(复合)的方式, 表现出的: 不再是"has-a", 而是: “is-implemented-in-terms-of”
即(通过xxx来实现), 即: 通过 (成员变量std::vector
), 来实现, 我们的My_Set这个类
37. never redefine a function’s inherited default parameter value
当一个类Son, 从他的父类Fa, 已经继承了一个名为func
的函数, 然后自己Son又定义了一个func
函数
此时一定有: (func为虚函数 否则, 违背36条例)
而, Fa::func( int a = 123);
, 假如这个虚函数, 是有 (缺省参数)的!!
此时, 如果Son::func( int a); 或者 func( int a = 666);
, 这就会引起歧义;
(虚函数的作用是: 动态绑定)
而, ( 函数的"缺省参数", 是: 静态绑定)
最好的处理是: (虚函数里, 不要有 “缺省参数”)
36. never redefine an inherited non-virtual function
子类中, 不要定义一个, 从父类继承而来的 非虚函数 的函数
即, 在继承体系里, 非虚函数, 不可以有 (相同的名称);
比如, 父类有一个func的非虚函数, 子类不可以再定义func这个名称
注意, 以下讨论时, 仅涉及函数名
, 无关(返回值和参数); 因为我们这里探讨的是: (函数名遮掩) 的问题
举个例子:
class Fa{
public:
void f0();
};
class Son : public Fa{
public:
int f0( int);
}
Son s;
Son * p0 = &s;
Fa * p1 = &s;
以上代码虽然没有错, 但是, (由于, f0函数不是虚函数, 即, 他会是: static bound
静态绑定)
如果是虚函数, 他是动态绑定, 即p0和p1 的f0函数, 会是同一个
但是此时, 由于不是虚函数, p0的f0函数 是Son的, p1的f0函数 是Fa的
而, 根据 32条款
, public继承, 呈现的是: is-a
的关系
- 如果子类要重新父类函数, 则使用 (虚函数)
- 那么, 对于(非虚函数), 即, 说明, 不存在 (重写)的关系; 当然, 此时有2种情况;
- 未继承下来; (比如, 父类有一个
func
函数, 但是, 子类的继承权限不够, 没有继承下来 父类的func)
那么, 子类定义 一个名为func的函数, 这没问题; 不存在 函数遮掩的问题 - 继承下来了;
0, (因为, 这个父类的func函数, 他不是virtual, 说明该函数不支持重写)
1, (又因为, 这个函数 继承下来了; 所谓继承, 不就是想调用 父类的函数嘛)
由0,1 分析, 子类要使用这个func函数, 就应该是: 父类的func函数, 子类不可以再定义!!!
- 未继承下来; (比如, 父类有一个
即, 对于一个(非virtual)的 函数func, 父类有一个func该函数一定是实现了的
,
如果子类 的继承权限, 把父类func 给继承下来了, 则, 子类, 绝对不可以写一个 (重名)的 func函数; {否则造成严重的歧义}
32. make sure public inheritance models is-a
当使用public继承
时, 就要想到: Student : public Person
这个例子, 即 学生, 拥有 "人"的 所有特性
编译器会在必要时, 会自动的, 将(学生) 强转为 (人); 0
确保你的 (publicj继承), 塑造出的 是一种 (is-a
)的 关系
当出现: class A : public B{}
, 此时 (不管有没有virtual函数), 就意味着: A
is a B
即, AB构成: is a
关系
什么是is a
关系呢? 即(是一种)
比如: student
is a person
因为, 但以public
继承时, 子类里的那个父类对象, 可以使用所有的public函数, 这和 单独一个父类对象的访问权限 完全相同;
所以, 你需要满足: 在逻辑上, A is a B 的前提下, 才可以去使用: A : public B
的继承;
如果逻辑上行不通, 则不要public继承
因为我们知道, 任何子类 是完全可以强转为 父类的 (因为, 子类里, 就有一个父类对象)
35. consider alternatives to virtual functions
假设有很多的(人物Character), 每个人物, 都有一个 (获取血量)的函数;
方式0: virtual
class Character_Base{
public:
virtual int get_health() = 0;
};
class Character_Hero{
public:
virtual int get_health(); // override
};
这是很自然的设计方案
方式1: NVI: non-virtual interface
, NVI思想 称: 所有的接口即对外的public
, 都不是 virtual.
(即所有virtual函数, 都是private)
class Character_Base{
public;
int get_health(){
// 0
int ret = get_health_do();
// 1
return ret;
}
private: // 注意这里是private
virtual int get_health_do() = 0;
};
class Character_Hero{
private: // 注意这里是private
virtual int get_health_do(); // override
};
和(方式0) 基本差不多, 只是: (将virtual函数 均改为private), (在父类, 统一设置public对外函数)
- 所有的子类, 都是使用的
父类::get_health()
的这个public函数
子类, 只需要 实现 (private的虚函数)
这个public
的 父类::get_health()
, 统一的 对外函数, 这个函数 也称为 虚函数的 wrapper
;
这样设计的好处是: (又封装了一