这一条款是说的是公有继承的逻辑,如果使用继承,而且继承是公有继承的话,一定要确保子类是一种父类(is-a关系)。这种逻辑可能与生活中的常理不相符,比如企鹅是生蛋的,所有企鹅是鸟类的一种,直观来看,我们可以用公有继承描述:

 class Bird
{
public:
virtual void fly(){cout << "it can fly." << endl;}
}; class Penguin: public Bird
{
// fly()被继承过来了,可以覆写一个企鹅的fly()方法,也可以直接用父类的
}; int main()
{
Penguin p;
p.fly(); // 问题是企鹅并不会飞!
}

但问题就来了,虽然企鹅是鸟,但鸟会飞的技能并不适用于企鹅,该怎么解决这个问题呢?方法一,在Penguin的fly()方法里面抛出异常,一旦调用了p.fly(),那么就会在运行时捕捉到这个异常。这个方法不怎么好,因为它要在运行时才发现问题。

方法二,去掉Bird的fly()方法,在中间加上一层FlyingBird类(有fly()方法)与NotFlyingBird类(没有fly()方法),然后让企鹅继承与NotFlyingBird类。这个方法也不好,因为会使注意力分散,继承的层次加深也会使代码难懂和难以维护。

方法三,保留所有Bird一定会有的共性(比如生蛋和孵化),去掉Bird的fly()方法,只在其他可以飞的鸟的子类里面单独写这个方法。这是一种比较好的选择,因为根本没有定义fly()方法,所以Penguin对象调用fly()会在编译期报错。

在艰难选择方法三之后,我们回过头来思考,就是在所有public继承的背后,一定要保证父类的所有特性子类都可以满足(父类能飞,子类一定可以飞),抽象起来说,就是在可以使用父类的地方,都一定可以使用子类去替换。

这正是Liskov替代原则告诉我们的:任何父类可以出现的地方,子类一定可以替代这个父类,只有当替换使软件功能不受影响时,父类才可以真正被复用。通俗地说,是“子类可以扩展父类的功能,但不能改变父类原有的功能”。

下面再来看一个数学上的例子,对于基础几何里的长方形和正方形,老师会说“正方形是特殊的长方形”,“特殊”体现在长宽是相等的,从字面上来看,我们可以表达成is-a关系,像这样:

 class Rectangle
{
protected:
int length;
int width;
public:
void virtual IncreaseLength(int DeltaLength)
{
length += DeltaLength;
}
}; class Square: public Rectangle
{
public:
void virtual IncreaseLength(int DeltaLength)
{
length += DeltaLength;
width += DeltaLength;
}
};

为了与保持正方形长宽等值的特性,不得不将IncreaseLength里面也对width进行了操作,到这一步,恐怕成员函数名IncreaseLength已经变味了,明明是扩展长度,但“偷偷地”也把宽度给变了。这就与“子类可以扩展父类的功能,但不能改变父类原有的功能”相背离了,所以这个is-a关系在数学世界里也可这样说说,但在程序的世界里并不成立!

举上面两个例子,足可以看出public继承并不像口头上说的那么容易,要去理解两个类是否可以真正成为is-a关系,还需要好好斟酌。最后总结一下:

“public继承”意味着is-a。适用于base classes身上的每一件事情一定也适用于derived classes身上,因为每一个derived class对象也都是一个base class对象。

读书笔记_Effective_C++_条款三十二:确定你的public继承继承塑模出is-a关系的更多相关文章

  1. 读书笔记_Effective_C++_条款三十九:明智而审慎地使用private继承

    private继承的意义在于“be implemented in turns of”,这个与上一条款中说的复合模型的第二层含义是相同的,这也意味着通常我们可以在这两种设计方法之间转换,但书上还是更提倡 ...

  2. 读书笔记_Effective_C++_条款三十四:区分接口继承和实现继承

    这个条款书上内容说的篇幅比较多,但其实思想并不复杂.只要能理解三句话即可,第一句话是:纯虚函数只继承接口:第二句话是:虚函数既继承接口,也提供了一份默认实现:第三句话是:普通函数既继承接口,也强制继承 ...

  3. 读书笔记_Effective_C++_条款三十:了解inline的里里外外

    学过基本程序课的同学都知道,inline是内联的关键字,它可以建议编译器将函数的每一个调用都用函数本体替换.这是一种以空间换时间的做法.把每一次调用都用本体替换,无疑会使代码膨胀,但可以节省函数调用的 ...

  4. 读书笔记_Effective_C++_条款三十六:绝不重新定义继承而来的non-virtual函数

    这个条款的内容很简单,见下面的示例: class BaseClass { public: void NonVirtualFunction() { cout << "BaseCla ...

  5. 读书笔记_Effective_C++_条款四十二:了解typename的双重意义

    顾名思义,typename有双重含意.只要你用过template,那么第一重含意一定知道,那就是声明模板的时候,我们既可以这样写: template <class T> 也可以这样写 te ...

  6. 读书笔记_Effective_C++_条款三十八:通过复合塑模出has-a或者is-implemented-in-terms-of

    如果说public是一种is-a的关系的话,那么复合就是has-a的关系.直观来说,复合就是在一个类中采用其他类的对象作为自身的成员变量,可以举个例子,像下面这样: class Person { pr ...

  7. 读书笔记_Effective_C++_条款三十五:考虑virtual函数以外的其他选择

    举书上的例子,考虑一个virtual函数的应用实例: class GameCharacter { private: int BaseHealth; public: virtual int GetHea ...

  8. 读书笔记_Effective_C++_条款四十五:运用成员函数模板接受所有兼容类型

    比如有一个Base类和一个Derived类,像下面这样: class BaseClass {…}; class DerivedClass : public BaseClass {…}; 因为是父类与子 ...

  9. 读书笔记_Effective_C++_条款四十九:了解new_handler的行为

    本章开始讨论内存分配的一些用法,C/C++内存分配采用new和delete.在new申请内存时,可能会遇到的一种情况就是,内存不够了,这时候会抛出out of memory的异常.有的时候,我们希望能 ...

随机推荐

  1. 谈谈.NET MVC QMVC高级开发

    自从吾修主页上发布了QMVC1.0,非常感兴趣,用了半月的时间学习,真的感觉收益非浅,在此声明非常感谢吾修大哥的分享! 1.轻快简单,框架就几个类,简单,当然代码少也就运行快!单纯的MVC,使的如果你 ...

  2. PHP的数据库连接mysqli遍历示例

    $mysqli = mysqli_init(); $mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 2);//设置超时时间,以秒为单位的连接超时时间 $m ...

  3. ubuntu下将程序挂后台命令

    ubuntu下将程序挂后台命令 nohup python -u main.py > test.out 2>&1 & ubunut下查看后台进程 jobs -l

  4. mac下---charles抓包https

    网上找的很多安装包都有问题,终于找到个可用的! 下载地址:  http://pan.baidu.com/s/1pLAONbX ———————————————————————————— 教程转载:htt ...

  5. 虚拟机 SUSE Linux Enterprise Server 12 SP2 64

    下载地址:https://www.suse.com/zh-cn/products/server/download/ 下载以后使用虚拟机安装即可

  6. Centos之命令搜索命令whereis与which

    Centos之命令搜索命令whereis与which whereis 命令名 #搜索命令所在路径及帮助文档所在位置 选项: -b :只查找可执行文件位置 -m:只查找帮助文件 [root@localh ...

  7. Centos之链接命令

    链接命令:ln  (link) ln -s [源文件] [目标文件] 功能描述:生成链接文件 选项: -s 创建软链接 硬链接特征: 1,拥有相同的i节点和存储block块,可以看作是同一个文件: 2 ...

  8. linux 系统网卡无法识别,缺少驱动

    #linux网卡驱动安装# Linux设备加载 #lsmod Module Size Used by e1000e 查看硬件设备 ls /usr/share/hwdata 查看pci网卡设备 lspc ...

  9. MySQL通过rpm安装及其单机多实例部署

    1. CentOS 下安装 MySQL Oracle 收购 MySQL 后,CentOS 为避免 MySQL 闭源的风险,改用 MySQL 的分支 MariaDB:MariaDB 完全兼容 MySQL ...

  10. CCF CSP 201409-3 字符串匹配

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201409-3 字符串匹配 问题描述 给出一个字符串和多行文字,在这些文字中找到字符串出现的那 ...