(一个)

class Shape {
public:
virtual void draw() const = 0;
virtual void error(const string& msg);
int objectID() const;
};
class Rectangle : public Shape {...};
class Ellipse : public Shape {...};

公有继承的概念看似简单。似乎非常轻易就浮出水面。然而细致审度之后,我们会发现公有继承的概念实际上包括两个相互独立的部分:函数接口的继承和函数实现的继承。二者之间的区别恰与函数声明和函数实现之间相异之处等价。成员函数的接口总是被继承,由于public继承意味is-a。

pure virtual 函数两个突出特征:(1)必须被不论什么“继承了它们”的具象class又一次声明。(2)它们在抽象class中通常未定义。

(二)

声明一个pure
virtual函数的目的是为了让derived class仅仅继承函数接口。我能够为它提供一份实现代码,但调用它的唯一途径是“调用时明白指出期class的名称”。

Sharp* ps1 = new Rectangle;
ps1->draw();
Sharp* ps2 = new Rectangle;
ps2->draw();
ps1->Shape::draw();//调用Shape的draw
ps2->Shape::draw();

(三)

声明impure
virtual函数的目的。是让derived classes继承该函数的接口和缺省实现。

同意impure
virtual函数同一时候指定函数声明和函数缺省行为,却有可能造成危急。

class Airport {...};
class Airplane {
public:
virtual void fly(const Airport& destination);
};
void Airplane::fly(const Airport& destination) {
//缺省代码,将飞机飞至指定目的地
}
class ModelA : public Airplane {...};
class ModelB : public Airplane {...};
class ModelC : public Airplane {
...//未声明fly函数,但它并不希望缺省飞行
};
Airport PDX(...);
Airplane* pa = new ModelC;
...
pa->fly(PDX); //未说出“我要的情况下就继承了该缺省行为,酿成大灾难”

这个程序试图用ModelA和ModelB的飞行方式来飞ModelC。

问题不在Airplane::fly()有缺省行为,而在于ModelC在未明白说出“我要”的情况下就继承了该缺省行为。我们能够做到“提供缺省实现给derived classes。但除非它们明白要求,否则免谈”。

解决的方法(1):此间伎俩在于切断“virtual
函数接口”和其“缺省实现”之间的连接。以下是一种做法:

class Airplane {
public:
virtual void fly(const Airport& destination) = 0;
...
protected:
void defaultFly(const Airport& destination);
};
void Airplane::defaultFly(const Airport& destination) {
//缺省行为,将飞机飞至目的地
}

fly已被改成为一个pure
virtual函数,仅仅提供飞行接口。缺省行为以defaultFly出如今Airplane class中。

若想使用缺省实现(比如ModelA和ModelB)。能够在fly中对defaultFly做一个inline调用:

class ModelA: public Airplane {
public:
virtual void fly(const Airport& destination) {
defaultFly(destination);
}
...
};
class ModelB: public Airplane {
public:
virtual void fly(const Airport& destination) {
defaultFly(destination);
}
...
};

如今ModelC不可能意外继承不对的fly实现代码了。由于Airplane中的pure
virtual函数迫使ModelC必须提供自己的fly版本号:

class ModelC: public Airplane {
public:
virtual void fly(const Airport& destination);
...
};
void ModelC::fly(const Airport& destination) {
//将C型飞机飞至指定的目的地
}

像这种话,这个方法并不是安全无虞。程序猿还是可能由于剪贴(copy-and-paste)代码而招来麻烦,但它比原来的设计值得依赖。

有些人反对以不同的函数分别提供接口和缺省实现,向上述的fly和defaultFly那样。

所以我们想出了以下这样的解决的方法:

解决的方法(2):

我们能够利用“pure
virtual函数必须在derived class中又一次声明。但它们能够拥有自己的实现”这一事实。

以下是Airplane继承体系怎样给pure virtual函数一份定义:

class Airplane {
public:
virtual void fly(const Airport& destination) = 0;
...
};
void Airplane::fly(const Airport& destination) // pure virtual 函数实现 {
//缺省行为,将飞机飞至指定目的地
}
class ModelA: public Airplane {
public:
virtual void fly(const Airport& destination) {
Airplane::fly(destination);
}
};
class ModelB: public Airplane {
public:
virtual void fly(const Airport& destination) {
Airplane::fly(destination);
}
};
class ModelC: public Airplane {
public:
virtual void fly(const Airport& destination)
...
};
void ModelC::fly(const Airport& destination) {
// 将C型飞机飞至指定目的地
}

(四)

假设成员函数是个non-virtual函数,意味着它并不打算在derived classes中有不同的行为。non-virtual 成员函数所表现的不变性凌驾其特异性,不管derived
class变得多么特异化。它的行为都不能够改变。

声明non-virtual函数的目的是为了令derived
class继承函数的接口及一份强制性实现。

class Shape {
public:
...
int objectID() const;
};

来看Shape::objectID的声明:能够想做是“每一个Shape对象都有一个用来产生对象识别码的函数:此识别码总是採用同样计算方法,该方法由Shape::objectID的定义式决定,不论什么derived
class都不应该尝试改变其行为”。

请记住:

1.  接口继承&实现继承不同。在public继承之下。derived classes总是继承base
class的接口。

2.  Pure virtual函数仅仅详细指定接口继承。

3.  Impure virtual函数详细指定接口继承及缺省实现继承。

4.  non-virtual函数详细指定接口继承以及强制性实现继承。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

Effective C++:规定34:区分接口继承和实现继承的更多相关文章

  1. Effective C++ Item 34 区分接口继承与实现继承

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 关联条款 Item 36 接口继承和实现继承不同.在 public 继承下, derived ...

  2. 读书笔记 effective c++ Item 34 区分接口继承和实现继承

    看上去最为简单的(public)继承的概念由两个单独部分组成:函数接口的继承和函数模板继承.这两种继承之间的区别同本书介绍部分讨论的函数声明和函数定义之间的区别完全对应. 1. 类函数的三种实现 作为 ...

  3. Effective C++ 34 区分接口继承和实现继承

    public继承从根本上讲,有两部分:接口继承和实现继承.两者之前的区别很像函数声明与函数定义. 具体设计中,会呈现三种形式:derived class只继承成员函数的接口(纯虚函数):derived ...

  4. Effective C++ -----条款34:区分接口继承和实现继承

    接口继承和实现继承不同.在public继承之下,derived classes总是继承base class的接口. pure virtual函数只具体指定接口继承. 简朴的(非纯)impure vir ...

  5. 条款34:区分接口继承和实现继承(Different between inheritance of interface and inheritance of implemenation)

    NOTE: 1.接口继承和实现继承不同.在public继承之下,derived classes总是继承base class的接口. 2.pure virtual 函数只具体指定接口继承及缺省实现继承. ...

  6. [EffectiveC++]item34:区分接口继承和实现继承

    [EffectiveC++]item34:区分接口继承和实现继承

  7. 【C++】Item34.区分接口继承和实现继承

    区分接口继承和实现继承 类包含的成员函数种类 1.静态函数 2.非静态函数 2.1 普通函数(非虚) non-virtual 2.2 虚函数 2.2.1 纯虚函数 pure-virtual 2.2.2 ...

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

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

  9. C++接口继承与实现继承的区别和选择

    1.接口继承与实现继承的区别 <Effective C++>条款三十四:区分接口继承和实现继承中介绍的比较啰嗦,概括地说需要理解三点: (1)纯虚函数只提供接口继承,但可以被实现: (2) ...

随机推荐

  1. java中三种常见内存溢出错误的处理方法(good)

    相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识. 在解决j ...

  2. JAVA学习(二):JDK介绍及其结构、用途

    JDK介绍及其结构.用途 1.JDK的介绍 JDK是Java Development Kit 的缩写.是Sun Microsystems针对Java开发员的产品.它由一个处于操作系统层之上的执行环境还 ...

  3. 转让malloc()该功能后,发生了什么事内核?附malloc()和free()实现源

    特此声明:在本文中,引用另一篇文章和帖子,结合的概括的理解malloc()函数的实现机制. 我们常常会在C程序中调用malloc()函数动态分配一块连续的内存空间并使用它们.那么,这些用户空间发生的事 ...

  4. WPF学习(5)依赖属性

    今天我们来学习WPF一个比较重要的概念:依赖属性.这里推荐大家看看周永恒大哥的文章,讲的确实很不错.我理解的没那么深入,只能发表一下自己的浅见.提到依赖属性,不得不说我们经常使用的传统的.net属性, ...

  5. Maven构建Hadoop

    Maven构建Hadoop工程 阅读目录 序 Maven 安装 构建 示例下载 系列索引 序 上一篇,我们编写了第一个MapReduce,并且成功的运行了Job,Hadoop1.x是通过ant来管理工 ...

  6. 编程获取linuxservercpu、内存和磁盘使用

    proc文件系统简介 /proc文件系统是一个伪文件系统.它是唯一的,其中存储器,如果不采取外部存储空间. 它是文件系统提供了与内核进程进行通信的接口的方法.用程序能够通过/proc得到系统的信息.并 ...

  7. IIS7安装场景对照表

    原文 IIS7安装场景对照表 Default Server Install Components Server Manager Update Name Static Content IIS-Stati ...

  8. ArcGIS for Silverlight 地图卷帘

    原文:ArcGIS for Silverlight 地图卷帘 ArcGIS 地图卷帘 for Silverlight 地图卷帘,其实就是遮罩的效果,在Silverlight里实现这样的效果,对于熟悉S ...

  9. 在ASP.NET2.0里打印网页指定的内容(比如打印网页里的一个Table)

    原文:在ASP.NET2.0里打印网页指定的内容(比如打印网页里的一个Table) 打印指定内容: <html> <head> <script   type= " ...

  10. Ubuntu 15.10 x64 安装 Android SDK(转)

    操作系统:Ubuntu 15.10 x64 目标:安装 Android SDK 本文最后更新时间:2015-11-3 安装32位库文件 2013年9月的iPhone 5s是第一款64位手机,而Andr ...