这一条款是说的是公有继承的逻辑,如果使用继承,而且继承是公有继承的话,一定要确保子类是一种父类(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. 爬虫基础---HTTP协议理解、网页的基础知识、爬虫的基本原理

    一.HTTP协议的理解 URL和URI 在学习HTTP之前我们需要了解一下URL.URI(精确的说明某资源的位置以及如果去访问它) URL:Universal Resource Locator 统一资 ...

  2. 查看sql语句加锁信息

    问题: 最近使用quartz集群,总是报deadlock问题,所以需要查看一下执行的sql导致的加锁冲突. 步骤: 1.在要测试的库中创建指定表innodb_lock_monitor create t ...

  3. python网络编程-socket样例

    socket样例 一:只能一个客户端发送一个信息. 客户端 # -*- coding:utf-8 -*- __author__ = 'shisanjun' import socket sock=soc ...

  4. HDU 3374 String Problem(KMP+最大(最小)表示)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3374 题目大意:给出一个字符串,依次左移一个单位形成一堆字符串,求其字典序最小和最大的字符串需要左移多 ...

  5. 组件库按需加载 借助babel-plugin-import实现

    前段时间一直在基于webpack进行前端资源包的瘦身.在项目中基于路由进行代码分离,http://www.cnblogs.com/legu/p/7251562.html.对于公司内部的组件库,所有内容 ...

  6. 【Codechef】Random Number Generator(多项式除法)

    题解 前置技能 1.多项式求逆 求\(f(x)\*g(x) \equiv 1 \pmod {x^{t}}\) 我们在t == 1时,有\(f[0] = frac{1}{g[0]}\) 之后呢,我们倍增 ...

  7. Linux 的软件安装目录

    Linux 的软件安装目录是也是有讲究的,理解这一点,在对系统管理是有益的 /usr:系统级的目录,可以理解为C:/Windows/,/usr/lib理解为C:/Windows/System32. / ...

  8. loadrunner——常见函数

    loadrunner——常见函数 分类: LoadRunner 2012-04-11 20:41 703人阅读 评论(0) 收藏 举报 loadrunnersearchweb服务器脚本stringwe ...

  9. Server sent passive reply with unroutable address. Using server address instead

    最近在linux服务器安装vsftp服务.经过一轮设置,终于可以连接上了,用winSCP连接,刷新目录就提示这个错误. 解决办法: vim /etc/vsftpd.conf  ,编辑配置文件,最后加上 ...

  10. 几种JS&CSS框架

    易用的,图形种类丰富的,美观的几种: 1.bootstrap 文档地址: http://www.cnblogs.com/fnng/p/4446047.html http://www.runoob.co ...