精华内容
下载资源
问答
  • 关系代数是一种抽象的查询语言,是关系数据操纵语言的一种传统表达方式,它用关系的运算来表达查询。运算对象、运算符、运算结果是运算的大要素。关系代数的运算对象是关系,运算结果亦为关系。关系代数中使用的...

    关系代数是一种抽象的查询语言,是关系数据操纵语言的一种传统表达方式,它用关系的运算来表达查询。运算对象、运算符、运算结果是运算的三大要素。关系代数的运算对象是关系,运算结果亦为关系。关系代数中使用的运算符包括4类:

    • 集合运算符:∪、-、∩、×

    • 专门的关系运算符:σ、Π、∞、÷

    • 比较运算符:>、≥、<、≤、=、≠

    • 逻辑运算符:非、与、或

    关系代数的运算按运算符的不同可分为传统的集合运算和专门的关系运算两类。其中,传统的集合运算将关系看成元组的集合,其运算是从关系的“水平”方向即行的角度进行的,而专门的关系运算不仅涉及行,而且涉及列。比较运算符和逻辑运算符是用来辅助专门的关系运算进行操作的。

    传统的集合运算

    传统的集合运算是二目运算,包括并、交、差、广义笛卡尔积4种运算。

    设关系R和关系S具有相同的目n,且相应的属性取自同一个域。

    # 并(Union)
    
    其结果仍为n目关系,由属于R或属于S的元组组成。
    
    # 差(Difference)
    
    其结果关系仍为n目关系,由属于R而不属于S的所有元组组成。
    
    # 交(Intersection)
    
    其结果关系仍为n目关系,由既属于R,又属于S的元组组成。关系的交可以用差来表示,即:R∩S=R-(R-S)。
    
    # 广义笛卡尔积(Extend Cartesian Product)
    两个分别为n目和m目的关系R和S的广义笛卡尔积是一个(n+m)列的元组的集合。
    元组的前n列是关系R的一个元组,后m列是关系S的一个元组。
    若R有k1个元组,S有k2个元组,则关系R和关系S的广义笛卡尔积有k1*k2个元组。
    

    专门的关系运算

    专门的关系运算包括选择、投影、连接、除等。

    # 选择(Selection)
    在关系R中选择满足给定条件的诸元组。
    从关系R中选取使逻辑表达式F为真的元组,这是从行的角度进行的运算。
    
    # 投影(Projection)
    从R中选择出若干属性列组成新的关系,是从列的角度进行的运算。
    投影之后不仅取消了原关系中的某些列,而且可能取消某些元组。
    
    # 连接(Join)
    从两个关系的笛卡尔积中选取满足属性间满足一定条件的元组。
    一般的连接操作是从行的角度进行运算的,而自然连接是同时从行和列的角度进行运算的。
    等值连接:从关系R与S的笛卡尔积中选取A、B属性值相等的那些元组
    自然连接:是一种特殊的等值连接,它要求两个关系中进行比较的分量必须使相同的属性组,并且在结果中把重复的属性去掉。
    
    # 除(Division)
    表示R中属性组X上值为x的诸元组在Z上分量的集合。
    除操作是同时从行和列的角度进行的运算。除操作适合于包含“对于所有的/全部的”语句的查询操作。
    

    在关系代数中,关系代数运算经过有限次复合后形成的式子称为关系代数表达式

    (最近更新:2019年09月03日)

    展开全文
  • C#数据结构

    2013-12-10 11:49:54
    数据类型可分为:一是非结构的原子类型,如C#语言中的基本类型 (整型、实型、字符型等);另一是结构类型,它的成分可以由多个结构类型 组成,并可以分解。结构类型的成分可以是非结构的,也可以是结构的。...
  • 关系抽取 实体之间的关系是知识图谱中不或缺的部分,不同的关系将独立的实体连接。...关系抽取分为以下种: 面向结构化文本的关系抽取 结构数据包括表格数据,XML文档以及数据库数据等,这数...

    关系抽取

    实体之间的关系是知识图谱中不可或缺的部分,不同的关系将独立的实体连接。关系抽取是文本内容理解的重要支撑技术,能够将文本分析从语言层面提升到内容层面,对于问答系统、智能客服、聊天机器人、语义搜索等应用都十分重要。

    任务概述

    • 任务定义
      定义为两个或多个实体之间的某种联系。
    • 任务分类
      关系抽取分为以下三种:
      • 面向结构化文本的关系抽取
        结构数据包括表格数据,XML文档以及数据库数据等,这类数据具有良好的布局结构,可以编写模板进行抽取。
      • 面向非结构haul文本的关系抽取
        纯文本,但是由于自然语言表达的多样性、灵活性,实体关系在文本中找不到明确的标识。准确率比较低。
      • 面向半结构化文本抽取
        布局有一定规律,但是也有多样性,需要用模板进行自动学习。
        根据抽取文本的范围不同,可分为
      • 句子级关系抽取
        从句中判断两个实体之间的关系
      • 语料集关系抽取
        两个实体之间是否存在某种语义关系,而不必限定两个目标实体所在的上下文。
        根据领域不同,分为:
      • 限定领域抽取
        在一个或多个限定的领域内对实体间语义关系进行抽取
      • 开发领域抽取
        不限定关系类别
    • 任务难点
    1. 同一个关系可以具有多种不同的词汇表发方法。如姚明出生于上海和姚明出生地在上海都表达了出生地的关系。
    2. 同一个短语或词可能表达不同的关系。如李梅是我的姑娘。代表了不同的关系,可以是女朋友也可以是女儿。
    3. 同一对实体之间可能存在不止一种关系。如姚明的出生地在上海,居住地也在上海。
    4. 关系抽取不仅涉及到两个或两个以上的实体单元,还涉及到周围的上下文。例如三国时期,蜀国有多位能征善战的将军,如关羽,张飞,赵云等
    5. 关系有时在文本中找不到任何明确的标识,隐含在文本中。如库克与中国移动面谈,透露出他将带领苹果进一步拓展中国市场的讯号。没有说库克和苹果公司的关系,但是从带领苹果公司来说,推断库克是苹果的执行官。
    6. 关系抽取一般依赖于词法,句法等基本的自然语言处理工具。但是性能不高,低性能工具引入错误。

    限定域关系抽取

    在一个或者多个限定的领域内判别文本中所出现的实体指称之间是何种语义关系,且待判别的语义关系是预定义的。

    常作为一个分类任务。即输入一个句子以及标识句子中所出现的实体指称的条件下,系统将其分类到所属的语义类别上。

    基于模板的关系抽取方法

    通过人工编辑或者学习到的模板对文本中的实体关系进行抽取和判别
    如:
    X is acquired by Y
    X is purchased by Y
    X is bought by Y
    表示收购。当一个句子中所出现的实体指称的上下文文本 满足上述模板时,则认为具有这种关系。
    但是人不可能针对多类关系穷举所有的模板,需要用自动化的方法。

    • 如何学习用于抽取关系的模板
      自提升的方法,对于实体和模板进行联合迭代式的交替抽取和学习。
    • 如何将学习到的模板进行聚类
      不同的模板可能代表同一关系,进行聚类。
      受限于模板的质量和覆盖度。

    基于机器学习的关系抽取方法

    很多工作将关系抽取看成是一个分类问题,开始选用更成熟的方法。

    • 有监督关系抽取
      如何抽取特征

      • 基于特征工程的方法
        1. 特征提取
          提取词汇、句法和语义等特征,有效的集成起来,从而产生描述关系实例的各种局部和全局特征。
          词汇特征
          实体属性特征
          重叠特征
          依存句法特征
          句法特征
        2. 模型训练
          利用提取的特征训练分类模型。
        3. 关系抽取
          利用训练好的模型进行分类,完成关系抽取。
      • 基于核函数的方法
        不需要构造固有的特征向量。
        以结构树为构造对象,核函数可以在高维的特征空间中计算距离。
      • 基于神经网络的方法
        词向量的分布式表示
        词汇级别的特征
        句子级别的特征
        拼接得到特征进行网络输出,输入softmax分类器。
    • 弱监督关系抽取
      距离监督
      假设:
      如果两个实体之间存在某种关系,则所有包含这两个实体的句子都表达这种关系,这些句子的集合被称为一个包。

      • 训练数据的产生
        通过命名实体识别工具找出给定的非结构化文本中的所有实体,根据弱监督假设,将实体组成对,在知识库中查,有抽出为正样本,无为负样本。
      • 任务建模
        不知道哪些有用,但只要有1个有用,就给定标签,并不关心是每个示例的标签。
      • 分段卷积神经网络
        设计了分段最大池化层代替原先的池化层
      • 多示例训练
        克服噪声的影响。对包的示例预测,选概率最大的为包的标签。但是会忽略包中其他方法的信息,有其他的改进。

    开放域关系抽取

    • 语料的自动生成和分类器训练

    • 大规模关系三元组的抽取

    • 关系三元组可信度计算

    展开全文
  • C++继承简介

    2018-09-05 17:10:12
    C++中作为一种自定义类型的数据结构,在面向对象程序设计思想中有着相当重要的作用。代表一事物的集合,与现实社会相似,类型同样具有继承实现的可能...C++语言中将继承分为三种情况,即public、protected以...

    C++中类作为一种自定义类型的数据结构,在面向对象程序设计思想中有着相当重要的作用。类代表一类事物的集合,与现实社会相似,类类型同样具有继承实现的可能。现实社会中事物的继承性随处可见,如父子的继承关系等。

    C++软件开发中,继承机制有着很多的运用。通常在软件编程中,继承与多态性的结合运用可以增加软件的扩展性以及应用程序可重用性。C++语言中将继承分为三种情况,即public、protected以及private继承。不同的继承方式有着不同的限制规则,这点会在后续的继承层次结构中详细讲述。

    通常,C++编程中的继承采用public方式居多,不排除特殊情况下会使用另外两种继承方式。但是protected与private继承方式的使用复杂性可能造成不必要的出错。下面主要通过类public继承方式来讲述类继承的基本概念与实际应用。首先通过一个完整的public继承方式的实例直观的了解继承的实现。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1001_01.h、chapter1001_01.cpp、chapter1001_02.h、chapter1001_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    /**

    * 实例chapter1001

    * 源文件chapter1001_01.h chapter1001_01.cpp

    * chapter1001_02.h chapter1001_02.cpp

    * 水果类单继承方式

    */

    //水果类头文件chapter1001_01.h

    #include <iostream>
    
    using namespace std;
    
    
    
    class Fruit                                                                           //水果类Fruit定义
    
    {
    
             public:                                                               //水果类公开接口成员
    
                       Fruit(){}                                                    //水果类构造函数定义
    
                       ~Fruit(){}                                                  //水果类析构函数定义
    
                       voidshowFruitInfo();                                      //水果类信息打印函数
    
    };

    //水果类源文件chapter1001_01.cpp

    #include "chapter1001_01.h"
    
    
    
    void Fruit::showFruitInfo()                                      //水果类信息打印方法外部定义
    
    {
    
             cout<<"Thisis Fruit class!"<<endl;
    
    }
    
    //苹果类头文件,chapter1001_02.h继承至水果类
    
    #include "chapter1001_01.h"
    
    
    
    class Apple: public Fruit                                         //苹果类Apple定义,以public方式继承至Fruit类
    
    {
    
             public:                                                               //苹果类公开接口成员
    
                       Apple(){}                                                 //苹果类构造函数定义
    
                       ~Apple(){}                                                        //苹果类析构函数定义
    
                       voidshowAppleInfo();                          //苹果类信息显示函数
    
    };

    //苹果类源文件chapter1001_02.cpp

    #include "chapter1001_02.h"
    
    
    
    void Apple::showAppleInfo()                                 //苹果类信息显示函数外部定义
    
    {
    
             cout<<"Thisis Apple class!"<<endl;
    
    }
    
    
    
    int main()
    
    {
    
             Fruitfruit;                                                          //主程序中定义水果类对象fruit
    
             Appleapple;                                                    //定义苹果类对象apple
    
             fruit.showFruitInfo();                                      //使用水果类对象调用其打印信息方法
    
             apple.showFruitInfo();                                   //使用苹果类对象调用继承至水果类方法成员打印类信息
    
             apple.showAppleInfo();                                //使用苹果类对象调用自身方法成员打印类信息
    
             return0;
    
    }

    本实例主要通过水果类、苹果类演示类单继承方式应用情况。程序主要由4个文件组成,分别为水果类头文件、源文件以及具体苹果类头文件、源文件。具体程序剖析见程序注释与后面讲解。

    2.编辑makefile

    Linux平台下需要编译源文件为chapter1001_01.cpp、chapter1001_02.cpp,相关makefile工程文件编译命令编辑如下所示。

    #makefile

    OBJECTS= chapter1001_01.o chapter1001_02.o
    
    CC=g++
    
    #生成可执行程序Apple
    
    Apple: $(OBJECTS)
    
             $(CC)$(OBJECTS) -o Apple
    
    chapter1001_01.o: chapter1001_01.h
    
    chapter1001_02.o: chapter1001_02.h
    
    #清除源文件以外的操作命令
    
    clean:
    
             rm-f Apple core $(OBJECTS)
    
    submit:
    
             cp-f -r Apple ../bin
    
             cp-f -r *.h ../include

    上述makefile文件的编写是Linux系统下工程化的编译思想的体现。由于Linux系统下编译器采用的是命令执行形式,makefile文件的组织应用,有利于大型软件工程中代码的编译管理。

    以上make命令是执行makefile文件中编译命令。为了使makefile编译文件显得更加的灵活可扩展,此处在makefile文件中增加变量定义,将makefile中多处重复使用的部分提取出来,采用变量定义来表示。这样下次需要增加或者修改对应的文件只需要修改一次即可。上述文件中,主要将程序生成的.o工程文件定义为变量OBJECTS表示,将编译器g++定义为变量CC表示。为此下次需要增加文件或者换用编译命令只需要修改该变量定义即可。

    随后将变量运用到执行命令中,此处,采用另一种形式来定义代码文件编译命令。直接通过代码工程文件以及对应的头文件,让make命令自动去推导程序应该执行的编译命令。此处主要涉及两个工程文件的编译,即chapter1001_01.o和chapter1001_02.o,与其对应的代码文件分别为chapter1001_01.h、chapter1001_01.cpp、chapter1001_02.h以及chapter1001_02.cpp,主程序入口定义于chapter1001_02.cpp中。最后,为了清除当前目录下生成的程序以及出错产生的core等垃圾文件定义了clean选项,选项下执行rm –f,表示指定清楚随后的各种文件。

    上述实例中不仅采用工程化makefile文件编译方式,同时也采用多代码文件的方式来编写实例程序。本实例共涉及四个代码文件,两个头文件代码,以及两个对应的代码源文件。在软件编程中,代码文件划分应用也是大型软件工程中必不可少的部分,头文件与源代码文件的区分会让软件组织结构更加的合理。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件。随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [developer @localhost src]$ make

    g++ -c -o chapter1001_01.o chapter1001_01.cpp

    g++ -c -o chapter1001_02.o chapter1001_02.cpp

    g++ chapter1001_01.o chapter1001_02.o  -o Apple

    [developer @localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [developer @localhost src]$ cd ../bin

    [developer @localhost bin]$ ./Apple

    This is Fruit class!

    This is Fruit class!

    This is Apple class!

    通常在C++面向对象的应用开发中,应用程序的头文件主要包含类体的定义,以及程序需要使用的常量定义等。而源文件则主要是类方法成员的具体实现的场所。使用时只需要#include包含对应的头文件即可。

    4.程序剖析

    多文件的使用更多的优势之处会在后续更多实例中慢慢体现出来,这里只作简单的介绍。本实例中主要涉及两个类,即水果类Fruit和苹果类Apple。两个类之间为继承关系,由于水果类代表一类广泛的概念类型,而苹果类则代表了具体水果的一种类型,它们之间存在一定的关联。这种关联可以理解为继承关联,即苹果类是从水果类继承而来的。

    实例中四个代码文件,其中chapter1001_01.h与chapter1001_01.cpp为水果类定义文件。头文件chapter1001_01.h中包含类Fruit的定义,该类包含自定义空的构造函数与析构函数,以及一个打印自身信息的方法成员showFruitInfo(),暂不包括任何数据成员。chapter1001_01.cpp文件中主要定义实现类Fruit的方法成员,只包含chapter1001_01.h头文件,此处主要定义实现类方法成员showFruitInfo()。

    头文件chapter1001_02.h中定义Apple类,采用公开继承方式从水果类派生定义而开。此时需要包含水果类头文件chapter1001_01.h,因为此处需要使用到该类的声明定义。该文件中Apple类公开继承至水果类Fruit,即意味着Apple类拥有了基类的所有成员,同时在此基础上Apple中还定义了显示本信息的方法成员,该成员在chapter1001_02.cpp源文件中定义实现。

    主程序中首先定义基类对象fruit,此时必然调用其定义构造函数构造该类对象实例。随后定义其派生类对象apple,该对象拥有基类一切成员的同时还拥有自定义的成员。程序中首先对Fruit对象实例构造完毕后,调用其信息显示函数showFruitInfo(),打印本类基本信息。随后使用苹果类对象实例分别调用自Fruit类继承的该方法,以及该类本身的信息显示方法。

    本实例仅仅演示类公开继承的方式的实现,此处仅仅涉及继承中的方法成员情况,下面将会就继承的更多具体操作作详细讲述。

    10.2  C++类继承层次结构

    C++中继承体系是一种类似数据结构中树状的层次结构,在树状层次的继承体系中,始终脱离不了两个基本概念即基类与派生类。通常C++软件开发中,类可以作为两种方式存在。一种是作为单独存在的自定义类型参与程序处理。另一种则是使用类继承机制,类通常不是作为顶层基类出现就是作为基类的派生类而存在,而该派生类又可以作为其派生类的基类。

    10.2.1  C++继承体系简介

    前面已经说明过,C++类一般可以作为继承顶层的基类,或者作为基类的派生类存在。基类通常又称为父类,在软件系统中基类应该表示一类事物最通用的基本特性。例如上小节中的水果与苹果类的继承关系,水果类代表一类水果最常见的特性与操作。而苹果类则为水果的一种,具有具体更多的特性描述,通过继承下次需要增加另外一种水果类型会变得很方便。直接继承至水果类即可实现,这种分析方式在面向对象中有助于理解使用继承增加软件可扩展性的概念。

    C++中类的继承体系可以是多个基类,派生类继承至该多个基类,通常这种继承方式称为为多继承。另外一种方式即可以是单个基类,扩展至多个派生类并且多个派生类之间是在同一个层次上,互不相干,同时派生类又可以继续派生下去。以下通过分析软件学院人员组织机构继承体系为例,如图10.1所示。

     

    图10.1  软件学院机构简单继承体系图

    上述继承分析实例中最上层的基类表示软件学院人员。随后继承体系下出现并行同层次的单继承情况,即软件学院人员可以由行政人员、教师与学生成员组成。类Administration、类Teacher以及类Student直接继承至类SoftwareMember,这种继承方式称为单继承。

    而软件学院中通常有一种拥有双重身份的成员。如一名行政人员同时又是教师身份,此时该类需要同时继承行政类与教师类而来,表示该类人的双重身份。类AdministrationTeacher继承至类Administration与类Teacher,这种继承方式称为多继承。当然一个软件学院机构中成员不仅仅会涉及以上这些。本图仅仅突出整个机构中的小部分组织来演示继承体系中可能出现的情况,即单继承与多继承的概念。稍后会在后续小节中详细讲述。

    通常类继承机制的优势在于,现有不变的类类型基础之上创建新类,从而通过增加新类操作方法来达到添加新功能的目的。最常见的是库的应用开发中,开发者可以开发出一定范围内可重用的组件类。而这些组件类功能在应用时可以根据具体需要在不修改原类的基础上进行继承扩充。软件中类的继承层次分析方法应用很多,面向对象编程思想中掌握这种分层方法分析事物有助于设计出高可扩展性以及功能强大的软件系统。初学者可以通过分析现实世界中的一些继承实例来培养该方面的思维方式。

    10.2.2  单继承中的基类与派生类

    C++中单继承有着很广阔的运用,一般软件开发中通过继承现有功能类的方式设计出功能更强大的处理类。但是通常要求基类中封装一类事物中最普遍的属性与操作,供派生类继承并加入新的属性与操作进行扩展。下面依然通过水果类继承实例,加入操作方法与数据成员,了解继承体系中基类与派生类的使用。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1002_01.h、chapter1002_01.cpp、chapter1002_02.h、chapter1002_02.cpp与testMain.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    /**
    
    * 实例chapter1002
    
    * 源文件chapter1002_01.hchapter1002_01.cpp
    
    * chapter1002_02.h chapter1002_02.cpp testMain.cpp
    
    * 水果类单继承方式
    
    */
    
    //水果基类头文件chapter1002_01.h
    
    #ifndef FRUIT_H
    
    #define FRUIT_H
    
    #include <iostream>
    
    #include <string>
    
    using namespace std;
    
    
    
    class Fruit                                                                                              //水果类Fruit
    
    {
    
             public:
    
                       Fruit();                                                                        //水果类构造函数
    
                       ~Fruit();                                                                      //水果类析构函数
    
                       voidsetFruitName(const char* name);               //水果类设置水果名称方法成员
    
                       voidshowFruitInfo();                                                        //水果类打印输出水果信息方法成员
    
                       voideatFruit();                                                          //水果类吃水果方法成员
    
             protected:
    
                       charm_name[100];                                                //水果类私有保护数据成员,表示水果名称
    
    };
    
    #endif

    //水果基类源文件chapter1002_01.cpp

    #include "chapter1002_01.h"
    
    
    
    Fruit::Fruit()                                                                                  //水果类构造函数定义实现
    
    {
    
             cout<<"Fruitclass construction!"<<endl;                      //打印输出构造函数信息
    
    }
    
    Fruit::~Fruit()                                                                                //水果类析构函数
    
    {
    
             cout<<"Fruitclass destruction!"<<endl;                        //打印输出析构函数信息
    
    }
    
    void Fruit::setFruitName(const char* name)                         //设置水果类名称信息方法定义实现
    
    {
    
             strcpy(m_name,name);                                                   //通过拷贝的方式将传入的参数赋值到数据成员中
    
    }
    
    void Fruit::showFruitInfo()                                                         //打印输出水果类信息方法定义实现
    
    {
    
             cout<<"showFruit member m_name:"<<m_name<<endl;        //打印输出水果类名称成员
    
    }
    
    void Fruit::eatFruit()                                                                    //水果类吃水果方法成员定义实现
    
    {
    
             cout<<"eatFruit!"<<endl;                                                 //打印输出水果类吃水果方法实现信息
    
    }
    
    //苹果类头文件chapter1002_02.h
    
    #include "chapter1002_01.h"
    
    
    
    class Apple: public Fruit                                                            //苹果类Apple,公开继承至水果基类Fruit
    
    {
    
             public:
    
                       Apple();                                                                      //苹果类构造函数
    
                       ~Apple();                                                                   //苹果类析构函数
    
                       voidsetAppleName(const char* name);            //苹果类设置名称方法成员
    
                       voidshowAppleInfo();                                            //苹果类打印苹果信息方法成员
    
                       voideatFruit();                                                          //苹果类中吃水果方法成员
    
    };
    
    //苹果类源文件chapter1002_02.cpp
    
    #include "chapter1002_02.h"
    
    
    
    Apple::Apple()                                                                             //苹果类构造函数定义实现
    
    {
    
             cout<<"Appleclass construction!"<<endl;
    
    }
    
    Apple::~Apple()                                                                                    //苹果类析构函数定义实现
    
    {
    
             cout<<"Appleclass destruction!"<<endl;
    
    }
    
    void Apple::setAppleName(const char* name)                   //苹果类设置名称方法定义实现
    
    {
    
             strcpy(m_name,name);
    
    }
    
    void Apple::showAppleInfo()                                                   //苹果类打印输出苹果信息方法成员定义实现
    
    {
    
             cout<<"showApple member m_name"<<m_name<<endl;
    
    }
    
    void Apple::eatFruit()                                                                           //苹果类中吃苹果方法成员定义实现
    
    {
    
             cout<<"eatApple!"<<endl;
    
    }

    //主程序源文件testMain.cpp

    #include "chapter1002_02.h"
    
    /*主函数入口*/
    
    int main()
    
    {
    
             Fruitfruit;                                                                            //定义水果类对象fruit
    
             fruit.setFruitName("Fruit");                                                     //水果类对象fruit调用其设置名称方法
    
             fruit.showFruitInfo();                                                         //水果类对象fruit调用其打印水果信息方法
    
             fruit.eatFruit();                                                                    //水果类对象fruit调用其吃水果方法
    
             Appleapple;                                                                      //定义苹果类对象apple
    
             apple.setAppleName("Apple");                                               //苹果类对象apple调用其设置名称方法
    
             apple.showAppleInfo();                                                   //苹果类对象apple调用其打印苹果信息方法
    
             apple.eatFruit();                                                                 //苹果类对象apple调用其吃水果方法
    
    }

    本实例程序主要通过水果、苹果类演示单类继承方式。程序主要由主函数与水果、苹果类组成,相关类中除了相应的构造与析构后,还包含相应输出数据成员信息的方法。程序具体分析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下需要编译源文件为chapter1002_01.cpp、chapter1002_02.cpp、testMain.cpp,相关makefile工程文件编译命令编辑如下所示。

    OBJECTS=chapter1002_01.o chapter1002_02.otestMain.o
    
    CC=g++
    
    
    
    testMain: $(OBJECTS)
    
             $(CC)$(OBJECTS) -o testMain
    
    testMain.o:
    
    chapter1002_01.o: chapter1002_01.h
    
    chapter1002_02.o: chapter1002_02.h
    
    
    
    clean:
    
             rm-f testMain core $(OBJECTS)
    
    submit:
    
             cp-f -r testMain ../bin
    
             cp-f -r *.h ../include

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件。随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [billing@localhost src]$ make

    g++    -c -ochapter1002_01.o chapter1002_01.cpp

    g++    -c -ochapter1002_02.o chapter1002_02.cpp

    g++    -c -otestMain.o testMain.cpp

    g++ chapter1002_01.o chapter1002_02.o testMain.o-o testMain

    [billing@localhost src]$ make submit

    cp -f -r testMain ../bin

    cp -f -r *.h ../include

    [billing@localhost src]$ cd ../bin

    [billing@localhost bin]$ ./testMain

    Fruit class construction!

    show Fruit member m_name:Fruit

    eat Fruit!

    Fruit class construction!

    Apple class construction!

    show Apple member m_nameApple

    eat Apple!

    Apple class destruction!

    Fruit class destruction!

    Fruit class destruction!

    4.程序剖析

    本实例主要演示水果继承体系中单继承的基本实现。主要由两个类源文件组成包含水果基类以及其派生出的苹果类。苹果类采用public方式继承至基类水果类,其中基类中包含构造与析构函数、设置水果类名称方法、打印水果类信息方法以及吃水果动作方法接口的定义。派生出的苹果类除了拥有基类成员外根据需要添加了自身类的构造与析构函数、设置苹果类名称方法、打印苹果类信息方法以及吃水果动作方法接口定义。

    实例主程序源文件中,首先定义基类Fruit对象fruit。此时会调用其自定义构造函数构造对应的对象实例。构造函数中主要实现一条提示信息语句。随后通过该对象实例调用其方法成员setFruitName,根据传入的实参设置水果基类的保护数据成员。采用fruit对象调用showFruitInfo打印输出设置的水果基类的数据成员m_name。最后通过fruit对象调用其吃水果动作方法,该方法主要实现为打印执行该方法的提示信息。

    主程序中之后定义派生类苹果类对象实例apple,派生类由于继承基类所有成员。所以初始化构造派生类对象之前需要首先调用基类水果类的构造函数,即程序运行结果中首先会打印输出基类构造函数调用的提示信息。随后才调用派生苹果类构造信息。通过apple对象调用其setAppleName方法设置继承至基类水果类的保护数据成员,随后调用showAppleInfo方法打印输出该成员信息。最后则通过apple对象调用eatFruit方法打印输出属于吃苹果具体的信息提示。

    从上述实例中总结出单继承的一些特点。首先,单继承中基类对象定义操作与派生类无关,完全可以将基类作为一个单独的类来处理应用。其次派生类中通过public方式继承,包含了基类的所有的成员,其中保护数据成员m_name在派生类中拥有一份拷贝可以供其对象使用。另外派生类中对象实例构造时构造函数执行需要优先执行基类构造,同时对象实例退出时析构函数的调用顺序则正好相反。

    最后,派生类中允许定义与基类同样名称的方法成员。此时派生类对象调用同名称方法时,基类该方法成员将会被隐藏。上述实例中允许了同一个方法实现具体不同操作的作法,稍后下章会介绍虚函数在同样情况的应用情况处理。

    10.3  继承体系中的访问控制

    C++中单独类的访问控制在类章节中已经详细讲述过。但是由于继承拥有public、protected以及private三种方式,针对不同的继承方式,与类中访问控制结合起来,使得派生类的访问控制变得更加的复杂,下面针对三类继承方式详细讲述派生类不同的访问控制策略。

    10.3.1 public继承

    类采用public方式从基类派生,基类中的public与protected成员在派生类中依然保持public与protected成员访问规则。类继承体系中,基类本身的访问控制方式与单独类访问控制无异,即可以将基类本身看成单独存在的类去应用,因为继承应用其中一个重要的原因就是基类可能被其它程序使用。为了保留基类对象的完整性,通过对基类的继承,来定义新的类类型供使用。这样在保持原有应用程序不变的情况下可以扩充和丰富具体继承类的操作。所以,基类在继承体系中依然保持独立的访问控制。

    public继承中,派生类继承至基类,即拥有基类的所有成员基础上增加属于派生类的成员。此时针对派生类的访问控制增加了基类成员部分。public继承方式下,基类的public成员在派生类中依然保持public属性,即派生类中可以保持对public成员的访问权限。派生类对象可以在类体外直接访问基类以及派生类本身的公开成员,包括非静态方法成员、友元函数以及数据成员。

    而public继承方式下基类的protected成员在派生类中依然保持protected成员访问控制规则,即派生类中只有非静态方法成员以及友元可以直接访问。public继承方式下,基类的private成员在派生类中依然保持private成员特性,派生类中不允许访问基类的private成员。但是可以通过基类public或者protected成员来访问,为此保证基类的隐藏特性。下面通过一个完整实例演示公开继承访问控制方式。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1003_01.h、chapter1003_01.cpp、chapter1003_02.h、chapter1003_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    /**

    * 实例chapter1003

    * 源文件chapter1003_01.hchapter1003_01.cpp

    * chapter1003_02.h chapter1003_02.cpp

    * 水果类公开继承访问控制

    */

    //水果类头文件chapter1003_01.h

    #ifndef      FRUIT_H
    
    #define     FRUIT_H
    
    #include <iostream>
    
    #include <string>
    
    using namespace std;
    
    
    
    class Fruit                                                                                    //水果类Fruit定义
    
    {
    
             public:                                                                        //水果类公开方法成员
    
                       Fruit(){}                                                             //水果类构造函数定义
    
                       ~Fruit(){}                                                           //水果类析构函数定义
    
                       voidsetFruitId(int id);                                     //水果类设置水果编号方法成员
    
                       voidshowFruitId();                                         //水果类打印水果编号方法成员
    
             public:                                                                        //水果类公开数据成员
    
                       stringm_name;                                                       //字符串数据成员表示水果名称
    
             protected:                                                                  //水果类保护类型数据成员
    
                       intm_price;                                                     //整型数据成员表示水果价格
    
             private:                                                                       //水果类私有保护数据成员
    
                       intm_id;                                                           //整型数据成员表示水果编号
    
    };
    
    #endif
    
    //水果类源文件chapter1003_01.cpp
    
    #include "chapter1003_01.h"
    
    
    
    void Fruit::setFruitId(int id)                                                        //水果类公开设置水果编号函数定义,传入整型参数表示编号
    
    {
    
             m_id= id;
    
    }
    
    void Fruit::showFruitId()                                                   //水果类公开打印水果编号函数定义
    
    {
    
             cout<<"TheFruit id:"<<m_id<<endl;
    
    }
    
    //苹果类头文件chapter1003_02.h
    
    #ifndef APPLE_H
    
    #define APPLW_H
    
    #include "chapter1003_01.h"
    
    
    
    class Apple: public Fruit                                                  //公开继承至水果类的苹果类定义
    
    {
    
             public:                                                                        //苹果类公开方法成员
    
                       Apple(){}                                                           //苹果类构造函数
    
                       ~Apple(){}                                                                  //苹果类析构函数
    
                       voidsetApplePrice(int price);                       //苹果类设置苹果价格方法成员
    
                       voidshowApplePrice();                                 //苹果类打印苹果价格的方法成员
    
    };
    
    #endif

    //苹果类源文件chapter1003_02.cpp

    #include "chapter1003_02.h"
    
    
    
    void Apple::showApplePrice()                                        //苹果类打印苹果价格方法成员定义
    
    {
    
             cout<<"Thisis Apple price:"<<m_price<<endl;
    
    }
    
    void Apple::setApplePrice(int price)                              //苹果类设置苹果价格方法成员定义,传入整型参数表示价格
    
    {
    
             m_price= price;
    
    }
    
    int main()
    
    {
    
             Appleapple;                                                             //定义苹果类对象apple
    
             apple.m_name= "apple";                                               //对象apple访问继承至基类的公开数据成员m_name
    
             cout<<apple.m_name<<endl;                              //通过对象apple直接打印类公开成员m_name
    
             apple.setApplePrice(3);                                         //apple对象调用设置苹果价格方法
    
             apple.showApplePrice();                                       //apple对象调用打印苹果价格方法
    
             apple.setFruitId(1);                                                  //apple对象调用设置水果编号方法
    
             apple.showFruitId();                                                //apple对象调用打印水果编号方法
    
             return0;
    
    }

    本实例通过水果类单继承方式演示类继承中公共继承访问控制情况。程序由主函数与水果、苹果类组成。具体程序剖析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下其makefile编译文件中涉及两个头文件以及两个源代码文件,makefile文件编辑如下。

    OBJECTS= chapter1003_01.o chapter1003_02.o
    
    CC=g++
    
    
    
    Apple: $(OBJECTS)
    
             $(CC)$(OBJECTS) -o Apple
    
    chapter1003_01.o: chapter1003_01.h
    
    chapter1003_02.o: chapter1003_0.h
    
    
    
    clean:
    
             rm-f Apple core $(OBJECTS)

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [developer @localhost src]$ make

    g++    -c -ochapter1003_01.o chapter1003_01.cpp

    g++    -c -ochapter1003_02.o chapter1003_02.cpp

    g++ chapter1003_01.o chapter1003_02.o -o Apple

    [developer @localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [developer @localhost src]$ cd ../bin

    [developer @localhost bin]$ ./Apple

    apple

    This is Apple price:3

    The Fruit id:1

    4.程序剖析

    上述实例依然采用水果继承实例作为演示。基类水果类中定义三类不同权限控制成员,分别是公开接口成员,以及公开数据成员、保护数据成员与私有数据成员。实例目的是验证在其派生类Apple类中访问基类成员的控制规则。Fruit基类中公开接口分别为setFruitId()与showFruitId()两个方法成员。该方法成员主要访问其私有保护数据成员,设置以及打印该该保护成员表示的水果编号。

    派生而来的Apple类中除了继承至Fruit类所有成员之外,同时添加属于Apple类的方法成员setApplePrice()与showApplePrice()。分别用来在Apple类中设置、打印继承到的保护数据成员m_price。主函数中首先定义Apple类对象。由于公有继承原先基类的公开成员,在派生类中非静态函数、友元以及派生类外部函数中可以直接使用,所以随后直接采用Apple类对象实例调用其继承基类的数据成员m_name并直接赋值,最后打印输出。

    Apple对象实例apple调用设置苹果价格的方法成员。根据传入的实参来设置继承至基类保护类型数据成员,由于公开继承中保护成员在派生类中依然保持保护成员的访问控制特性。所以在派生类中自然可以通过其public类型方法成员操作。Apple对象实例调用完价格成员设置之后,调用其价格打印方法成员在屏幕上打印显示价格信息。

    由于public类型继承方式,私有的成员在派生类中都不可以直接操作。所以只能通过继承至基类的公开接口来操作其私有保护成员。上述实例中Apple类对象实例调用继承至Fruit类的公开方法成员setFruitId以及showFruitId方法,分别设置该基类私有数据成员编号值以及屏幕打印该值。

    10.3.2  protected继承

    当派生类采用protected方式继承至基类时,基类中的public与protected成员在派生类中都自动转变为protected成员。即此时只能通过派生类的非静态成员函数以及其友元函数可直接访问。而基类的私有保护成员在派生类中则被隐藏,只能通过调用基类的公开成员以及保护成员来访问。

    为了演示上述继承方式下成员访问控制,则上一小节实例可修改为protected继承方式。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1004_01.h、chapter1004_01.cpp、chapter1004_02.h、chapter1004_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    //水果类Fruit.h

    #ifndef FRUIT_H
    
    #define FRUIT_H
    
    #include <iostream>
    
    #include <string>
    
    using namespace std;
    
    
    
    class Fruit
    
    {
    
               public:
    
                      Fruit(){}
    
                      ~Fruit(){}
    
                      void setFruitId(int id);
    
                      void setFruitName(string name);
    
                      void showFruitId();
    
                      void showFruitName();
    
               public:
    
                      string m_name;
    
               protected:
    
                      int m_price;
    
               private:
    
                      int m_id;
    
    };
    
    #endif

    //水果类源文件Fruit.cpp

    #include "Fruit.h"
    
    
    
    void Fruit::setFruitId(int id)
    
    {
    
               m_id = id;
    
    }
    
    void Fruit::setFruitName(string name)
    
    {
    
               m_name = name;
    
    }
    
    void Fruit::showFruitId()
    
    {
    
               cout<<"The Fruitid:"<<m_id<<endl;
    
    }
    
    void Fruit::showFruitName()
    
    {
    
               cout<<"The Fruitname:"<<m_name<<endl;
    
    }

    //苹果类头文件Apple.h

    #ifndef APPLE_H
    
    #define APPLW_H
    
    #include "Fruit.h"
    
    
    
    class Apple: protected Fruit
    
    {
    
               public:
    
                     Apple(){}
    
                     ~Apple(){}
    
                     void setAppleInfo(int id,string name,intprice);
    
                     void showAppleInfo();
    
    };
    
    #endif

    //苹果类源文件Apple.cpp

    #include "Apple.h"
    
    
    
    void Apple::showAppleInfo()
    
    {
    
               showFruitId();
    
               showFruitName();
    
               cout<<"This is Appleprice:"<<m_price<<endl;
    
    }
    
    void Apple::setAppleInfo(int id,string name,intprice)
    
    {
    
               m_price = price;
    
               setFruitId(id);
    
               setFruitName(name);
    
    }
    
    
    
    int main()
    
    {
    
               Apple apple;
    
               apple.setAppleInfo(1,"Apple",3);
    
               apple.showAppleInfo();
    
               return 0;
    
    }

    本实例通过水果类单继承方式演示类继承中保护继承访问控制情况。程序由主函数与水果、苹果类组成。具体程序剖析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下其makefile编译文件中涉及两个头文件以及两个源代码文件,makefile文件编辑如下。

    OBJECTS=chapter1004_01.o chapter1004_02.o
    
    CC=g++
    
    
    
    Apple: $(OBJECTS)
    
             $(CC)$(OBJECTS) -o Apple
    
    chapter1004_01.o: chapter1004_01.h
    
    chapter1004_02.o: chapter1004_02.h
    
    
    
    clean:
    
             rm-f Apple core $(OBJECTS)
    
    submit:
    
             cp-f -r Apple ../bin
    
             cp-f -r *.h ../include

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [billing@localhost src]$ make

    g++    -c -ochapter1004_01.o chapter1004_01.cpp

    g++    -c -ochapter1004_02.o chapter1004_02.cpp

    g++ chapter1004_01.o chapter1004_02.o -o Apple

    [billing@localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [billing@localhost src]$ cd ../bin

    [billing@localhost bin]$ ./Apple

    The Fruit id:1

    The Fruit name:Apple

    This is Apple price:3

    本实例程序中,采用protected继承方式。由于基类中public与protected成员在派生类中统一演变为protected成员,所以基类中定义的setFruitId、setFruitName、showFruitId、与showFruitName方法成员在派生类中只能通过派生类自定义方法成员访问。所以派生类中增加setAppleInfo与showAppleInfo方法用于调用基类中相关公开接口成员,便于外界通过派生类对象间接调用。

    与上述实例不同的是,主程序中,基类public类型的数据成员m_name在派生类中已经变为protected成员,不能在派生类外部函数中直接访问。同时,基类公开的public成员也不能在派生类外部通过对象实例直接调用,而只能通过派生成员间接访问其基类公开成员。另外,基类中的私有数据成员也只能通过基类public与protected成员访问,protected继承方式实际上增加了了继承类中成员的访问控制力度。

    10.3.3  private继承

    类private继承方式则更加严格控制成员访问限度。基类的public成员在派生类中演变为private类型只能通过其非静态成员函数以及友元访问。另外,基类的protected成员在派生类中也演变为private私有类型,访问规则同上。基类中的private成员则与前面两种继承方式相同,派生类中此类数据被直接隐藏,只能通过基类公开以及保护方法成员去访问。

    类中private继承方式实例初学者可以根据前两个继承方式来修改后,执行验证其运行结果。至此类的三种继承方式介绍完毕,初学者可以通过自定义实例来验证不同种情况下类成员的访问控制规则,不断的提高自身对于继承体系概念的认识。

    10.4  继承体系中的构造与析构

    C++中继承概念前面基本已经有一个大致的轮廓。但是其中还涉及到一个重要的部分,即基类、派生类的构造函数与析构函数定义执行的情况,下面将会就该主题通过完整实例作详细讲述。

    10.4.1  派生类构造函数

    继承体系中对象实例的构造有着一定的执行顺序,主要针对派生类对象实例构造时,基类构造以及派生类构造函数的执行情况作详细讲述。下面通过一个派生类对象构造实例作为开端。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1005_01.h、chapter1005_01.cpp、chapter1005_02.h、chapter1005_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    //水果类头文件chapter1005_01.h

    #include <iostream>
    
    using namespace std;
    
    
    
    class Fruit
    
    {
    
             public:
    
                       Fruit();
    
                       voidshowFruitInfo();
    
    };

    //水果类源文件chapter1005_01.cpp

    #include "chapter1005_01.h"
    
    
    
    Fruit::Fruit()
    
    {
    
             cout<<"Fruitclass construction!"<<endl;
    
    }
    
    void Fruit::showFruitInfo()
    
    {
    
             cout<<"Thisis Fruit class!"<<endl;
    
    }

    //苹果类头文件chapter1005_02.h

    #include "chapter1005_01.h"
    
    
    
    class Apple: public Fruit
    
    {
    
             public:
    
                       Apple();
    
                       ~Apple(){}
    
    };

    //苹果类源文件chapter1005_02.cpp

    #include "chapter1005_02.h"
    
    
    
    Apple::Apple()
    
    {
    
             cout<<"Appleclass construction!"<<endl;
    
    }
    
    
    
    int main()
    
    {
    
             Fruitfruit;
    
             Appleapple;
    
             apple.showFruitInfo();
    
             return0;
    
    }

    本实例主要通过水果类继承方式演示继承体系中构造函数执行顺序情况。程序主要由主函数与水果、苹果类组成。程序具体剖析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下需要编译源文件为chapter1005_01.cpp、chapter1005_02.cpp,相关makefile工程文件编译命令编辑如下所示。

    OBJECTS=chapter1005_01.o chapter1005_02.o
    
    CC=g++
    
    
    
    Apple: $(OBJECTS)
    
             $(CC)$(OBJECTS) -o Apple
    
    chapter1005_01.o: chapter1005_01.h
    
    chapter1005_02.o: chapter1005_02.h
    
    
    
    clean:
    
             rm-f Apple core $(OBJECTS)
    
    submit:
    
             cp-f -r Apple ../bin
    
             cp-f -r *.h ../include

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [billing@localhost src]$ make

    g++    -c -ochapter1005_01.o chapter1005_01.cpp

    g++    -c -ochapter1005_02.o chapter1005_02.cpp

    g++ chapter1005_01.o chapter1005_02.o -o Apple

    [billing@localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [billing@localhost src]$ cd ../bin

    [billing@localhost bin]$ ./Apple

    Fruit class construction!

    Fruit class construction!

    Apple class construction!

    This is Fruit class!

    本实例共涉及两个类,即水果类与其派生类苹果类。其中水果类除了包含有自身的构造函数外还拥有一个打印本身类信息成员方法。其派生类Apple中,只拥有属于自身一个构造函数定义,另外就是继承至水果类的打印方法成员。

    主程序中首先定义水果类对象实例,此时根据类构造规则如果没有自定义构造函数,则调用默认构造来构造对象实例。而此处基类自定义了构造函数,则调用自定义构造函数,该函数中执行体只有打印其执行构造的信息“Fruit class construction!”。

    随后定义派生类对象,此时注意到,派生类对象实例构造会同时调用基类构造函数与派生类自身的构造函数,并且基类构造函数首先被执行,所以会先调用基类构造函数打印“Fruit class construction!”随后调用派生类构造函数打印信息“Apple class construction!”。最终调用继承至基类的打印类信息方法打印信息“This is Fruit class”从而结束程序运行。

    从上述实例表明,基类对象构造时保持类构造特性,而派生类对象实例构造时会优先调用基类构造函数。原因在于派生类中继承有基类的成员,派生类对象构造时需要优先构造初始化基类成员以供派生类使用。派生类对象实例构造时,优先处理基类构造函数,如果基类没有自定义构造函数则直接调用其默认构造即可。另外基类的构造函数可以由派生类单独显式调用,上述实例修改如下所示。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1006_01.h、chapter1006_01.cpp、chapter1006_02.h、chapter1006_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    //水果类头文件chapter1006_01.h

    #include <iostream>
    
    using namespace std;
    
    
    
    class Fruit                                                                                              //水果类Fruit定义
    
    {
    
             public:
    
                       Fruit(intid,string name);                                         //水果类构造函数,两个整型参数
    
             protected:
    
                       intm_id;                                                                    //水果类保护数据成员,表示水果编号
    
                       stringm_name;                                                                 //水果类保护数据成员,表示水果名称
    
    };

    //水果类源文件chapter1006_01.cpp

    #include "chapter1006_01.h"
    
    
    
    Fruit::Fruit(int id,string name)                                                   //水果类构造函数定义
    
    {
    
             m_id= id;
    
             m_name= name;
    
             cout<<"Fruitclass construction!"<<endl;
    
    }

    //苹果类头文件chapter1006_02.h

    #include "chapter1006_01.h"
    
    
    
    class Apple: public Fruit                                                            //继承至水果类的苹果类Apple定义
    
    {
    
             public:
    
                       Apple(intid,string name,int price);                       //苹果类构造函数
    
                       ~Apple(){}                                                                           //苹果类析构函数
    
                       voidshowAppleInfo();                                            //打印苹果类信息函数
    
             protected:
    
                       intm_price;                                                               //苹果类保护数据成员,表示苹果价格
    
    };

    //苹果类源文件chapter1006_02.cpp

    #include "chapter1006_02.h"
    
    
    
    Apple::Apple(int id,string name,int price)                              //苹果类构造函数定义
    
             :Fruit(id,name)
    
    {
    
             m_price= price;
    
             cout<<"Appleclass construction!"<<endl;
    
    }
    
    void Apple::showAppleInfo()                                                   //打印苹果类函数定义
    
    {
    
             cout<<"Fruitid:"<<m_id<<endl;
    
             cout<<"Fruitname:"<<m_name<<endl;
    
             cout<<"Fruitprice:"<<m_price<<endl;
    
    }
    
    
    
    int main()
    
    {
    
             Fruitfruit(1,"Fruit");                                                            //定义水果类对象实例,同时传入实参初始化
    
             Appleapple(2,"Apple",3);                                                //定义苹果类对象实例,同时传入实参初始化
    
             apple.showAppleInfo();                                                   //调用apple对象实例打印方法显示信息
    
             return0;
    
    }

    本实例主要通过水果类继承方式演示继承体系中构造函数执行顺序情况。程序主要由主函数与水果、苹果类组成。程序具体剖析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下其makefile编译文件中涉及两个头文件以及两个源代码文件,makefile文件编辑如下。

    OBJECTS=Fruit.o Apple.o
    
    CC=g++
    
    
    
    Apple: $(OBJECTS)
    
             $(CC)$(OBJECTS) -o Apple
    
    Fruit.o: Fruit.h
    
    Apple.o: Apple.h
    
    
    
    clean:
    
             rm-f Apple core $(OBJECTS)

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [developer @localhost src]$ make

    g++    -c -ochapter1006_01.o chapter1006_01.cpp

    g++    -c -ochapter1006_02.o chapter1006_02.cpp

    g++ chapter1006_01.o chapter1006_02.o -o Apple

    [developer @localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [developer @localhost src]$ cd ../bin

    [developer @localhost bin]$ ./Apple

    Fruit class construction!

    Fruit class construction!

    Apple class construction!

    Fruit id:2

    Fruit name:Apple

    Fruit price:3

    经过修改的实例中主要增加了基类的保护型数据成员以及自定义构造函数。其中,构造函数涉及两个基类成员的初始化工作。两个数据成员m_id与m_name分别表示水果类的编号与名称,构造函数定义中主要依据外界传入参数来初始化该数据成员。派生类中则除了继承至基类的保护型数据成员外,增加一个m_price数据成员表示水果的价格,同样其构造函数中实现根据参数传递初始化该值。

    所不同的是,在派生类Apple构造函数定义中,增加了显示调用基类构造函数的操作,通过“:”初始化参数列表的方式来调用基类的构造函数。同时将根据调用派生类构造函数传入的实参来作为基类构造函数实参初始化。主程序中首先定义Fruit类对象实例fruit,同时传入编号以及名称值构造并初始化对象实例。随后定义派生类对象apple,此时传入三个实参用来构造初始化,其中,前两个参数用于显式调用基类构造函数作为实参传入并初始化。最后在派生类中增加打印基类以及派生类所有保护数据成员信息,验证构造初始化工作的正确性。

    综上所述,继承体系中派生类构造对象实例时。首先调用基类的构造函数用于初始化基类成员。当基类没有自定义构造函数时,调用其默认构造函数。同时基类有多个构造函数重载时也可以在派生类中显式的指定调用基类哪个构造函数来构造对象实例。

    10.4.2  派生类析构函数

    C++类继承体系中析构函数的操作次序与其构造函数正巧相反。此时派生类的析构函数调用要在基类析构函数调用之前发生。尤其当基类中包含有对象成员时,通常是先调用对象成员构造函数后调用基类构造函数,最后调用派生类构造函数,而析构函数调用顺序刚好相反。修改上一小节实例增加析构函数简单说明继承中析构函数使用情况,修改后的代码如下所示。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1007_01.h、chapter1007_01.cpp、chapter1007_02.h、chapter1007_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    //水果类头文件chapter1007_01.h

    #include <iostream>
    
    using namespace std;
    
    
    
    class Fruit
    
    {
    
             public:
    
                       Fruit(intid,string name);
    
                       ~Fruit();
    
             protected:
    
                       intm_id;
    
                       stringm_name;
    
    };

    //水果类源文件chapter1007_01.cpp

    #include "chapter1007_01.h"
    
    
    
    Fruit::Fruit(int id,string name)
    
    {
    
             m_id= id;
    
             m_name= name;
    
             cout<<"Fruitclass construction!"<<endl;
    
    }
    
    Fruit::~Fruit()
    
    {
    
             cout<<"Fruitclass destruction!"<<endl;
    
    }

    //苹果头文件chapter1007_02.h

    #include "chapter1007_01.h"
    
    
    
    class Apple: public Fruit
    
    {
    
             public:
    
                       Apple(intid,string name,int price);
    
                       ~Apple();
    
                       voidshowAppleInfo();
    
             protected:
    
                       intm_price;
    
    };

    //苹果类源文件chapter1007_02.cpp

    #include "chapter1007_02.h"
    
    
    
    Apple::Apple(int id,string name,int price)
    
             :Fruit(id,name)
    
    {
    
             m_price= price;
    
             cout<<"Appleclass construction!"<<endl;
    
    }
    
    Apple::~Apple()
    
    {
    
             cout<<"Appleclass destruction!"<<endl;
    
    }
    
    void Apple::showAppleInfo()
    
    {
    
             cout<<"Fruitid:"<<m_id<<endl;
    
             cout<<"Fruitname:"<<m_name<<endl;
    
             cout<<"Fruitprice:"<<m_price<<endl;
    
    }
    
    
    
    int main()
    
    {
    
             Fruit fruit(1,"Fruit");
    
             Apple apple(2,"Apple",3);
    
             apple.showAppleInfo();
    
             return 0;
    
    }

    本实例主要通过水果类继承方式演示继承体系中析构函数执行顺序情况。程序主要由主函数与水果、苹果类组成。程序具体剖析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下其makefile编译文件中涉及两个头文件以及两个源代码文件,makefile文件编辑如下。

    OBJECTS=chapter1007_01.o chapter1007_02.o
    
    CC=g++
    
    
    
    Apple: $(OBJECTS)
    
             $(CC)$(OBJECTS) -o Apple
    
    chapter1007_01.o: chapter1007_01.h
    
    chapter1007_02.o: chapter1007_02.h
    
    
    
    clean:
    
             rm-f Apple core $(OBJECTS)
    
    submit:
    
             cp-f -r Apple ../bin
    
             cp-f -r *.h ../include

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [developer @localhost src]$ make

    g++    -c -ochapter1007_01.o chapter1007_01.cpp

    g++    -c -ochapter1007_02.o chapter1007_02.cpp

    g++ chapter1007_01.o chapter1007_02.o -o Apple

    [developer @localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [developer @localhost src]$ cd ../bin

    [developer @localhost bin]$ ./Apple

    Fruit class construction!

    Fruit class construction!

    Apple class construction!

    Fruit id:2

    Fruit name:Apple

    Fruit price:3

    Apple class destruction!

    Fruit class destruction!

    Fruit class destruction!

    与上小节运行结果不同的是,本实例增加自定义析构函数中提示信息。因此在主程序中对象生命周期结束后,优先调用其派生类析构函数打印信息“Apple class Destruction!”。随后调用基类析构函数析构方法,最后因为基类对象实例最先被定义,所以基类析构函数最后被执行。

    继承体系中构造析构函数的操作情况。建议初学者通过多写一些不同情况下处理构造析构的程序来验证总结继承体系中构造与析构执行顺序以及应用处理。

    10.5  多重继承

    C++中继承除了从单个基类派生以外还可以从多个基类派生继承创建新类。这种继承方式称为多重继承,下面将会详细讲述多重继承的基本应用情况。

    所谓多重继承,即派生类继承至多个基类,并且拥有其多个基类的成员。多重继承方式与单继承方式基本语法实现形式相同,仅仅是在继承实现时通过逗号分隔符隔开多个基类名,其一般形式如下。

    派生类: public/protected/private 基类1,基类2…

    派生类继承至多个基类,即派生类中拥有多个基类成员,当然基类的构造函数与析构函数除外。多重继承除了在定义形式上与单继承稍有不同,其余访问控制等操作运用基本与单继承一致。下面通过一个完整实例演示前面小节分析的软件学院机构人员继承层次,其中行政人员同时又可以是老师的身份部分就包括多重继承的运用,即该类同时具有行政人员类与教师类的特征。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1008_01.h、chapter1008_01.cpp、chapter1008_02.h、chapter1008_02.cpp、chapter1008_03.h、chapter1008_03.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    //行政人员类头文件chapter1008_01.h

    #ifndef ADMINISTRATION_H
    
    #define ADMINISTRATION_H
    
    #include <iostream>
    
    #include <string>
    
    using namespace std;
    
    
    
    class Administration
    
    {
    
             public:
    
                       Administration(){}
    
                       ~Administration(){}
    
                       voidsetAdministrationId(int id);
    
                       voidsetAdministrationName(string name);
    
             protected:
    
                       intm_AdministrationId;
    
                       stringm_AdministrationName;
    
    };
    
    #endif

    //行政人员类源文件chapter1008_01.cpp

    #include "chapter1008_01.h"
    
    
    
    void Administration::setAdministrationId(int id)
    
    {
    
             m_AdministrationId= id;
    
    }
    
    void Administration::setAdministrationName(stringname)
    
    {
    
             m_AdministrationName= name;
    
    }

    //教师类头文件chapter1008_02.h

    #ifndef TEACHER_H
    
    #define TEACHER_H
    
    #include <iostream>
    
    #include <string>
    
    using namespace std;
    
    
    
    class Teacher
    
    {
    
             public:
    
                       Teacher(){}
    
                       ~Teacher(){}
    
                       voidsetTeacherId(int id);
    
                       voidsetTeacherName(string name);
    
             protected:
    
                       intm_teacherId;
    
                       stringm_teacherName;
    
    };
    
    #endif

    //教师类源文件chapter1008_02.cpp

    #include "chapter1008_02.h"
    
    
    
    void Teacher::setTeacherId(int id)
    
    {
    
             m_teacherId= id;
    
    }
    
    void Teacher::setTeacherName(string name)
    
    {
    
             m_teacherName= name;
    
    }

    //行政管理同时又是教师身份人员类头文件chapter1008_03.h

    #ifndef ADMINISTRATIONTEACHER_H
    
    #define ADMINISTRATIONTEACHER_H
    
    #include "chapter1008_02.h"
    
    #include "chapter1008_01.h"
    
    
    
    class AdministrationTeacher:publicAdministration,public Teacher
    
    {
    
             public:
    
                       AdministrationTeacher(){}
    
                       ~AdministrationTeacher(){}
    
                       voidshowAdministrationTeacherInfo();
    
    };
    
    #endif

    //行政人员同时又是教师身份人员类的源文件chapter1008_03.cpp

    #include "chapter1008_03.h"
    
    
    
    voidAdministrationTeacher::showAdministrationTeacherInfo()
    
    {
    
             cout<<"TheAdministration id:"<<m_AdministrationId<<endl;
    
             cout<<"TheAdministration name:"<<m_AdministrationName<<endl;
    
             cout<<"TheTeacher id:"<<m_teacherId<<endl;
    
             cout<<"TheTeacher name:"<<m_teacherName<<endl;
    
    }
    
    int main()
    
    {
    
             AdministrationTeacheradministrationTeacher;
    
             administrationTeacher.setAdministrationId(1);
    
             administrationTeacher.setAdministrationName("jack");
    
             administrationTeacher.setTeacherId(2);
    
             administrationTeacher.setTeacherName("john");
    
             administrationTeacher.showAdministrationTeacherInfo();
    
             return0;
    
    }

    本实例程序主要通过教师类继承体系演示了多重继承的功能。程序主要由主函数与教师继承类组成,教师继承类体系中包含学校行政人员类、教师类以及两者身份兼有的类。程序具体剖析见程序注释与后面讲解。

    2.编辑makefile

    Linux平台下其makefile编译文件中涉及三个头文件以及三个源代码文件,makefile文件编辑如下。

    OBJECTS=chapter1008_01.o chapter1008_02.ochapter1008_03.o
    
    CC=g++
    
    
    
    chapter1008_03: $(OBJECTS)
    
             $(CC)$(OBJECTS) -o chapter1008_03
    
    chapter1008_01.o: chapter1008_01.h
    
    chapter1008_02.o: chapter1008_02.h
    
    chapter1008_03.o: chapter1008_03.h
    
    
    
    clean:
    
             rm-f chapter1008_03 core $(OBJECTS)
    
    submit:
    
             cp-f -r chapter1008_03 ../bin
    
             cp-f -r *.h ../include

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [developer @localhost src]$ make

    g++    -c -ochapter1008_01.o chapter1008_01.cpp

    g++    -c -ochapter1008_02.o chapter1008_02.cpp

    g++    -c -o chapter1008_03.o chapter1008_03.cpp

    g++ chapter1008_01.o chapter1008_02.ochapter1008_03.o -o chapter1008_03

    [developer @localhost src]$ make submit

    cp -f -r chapter1008_03 ../bin

    cp -f -r *.h ../include

    [developer @localhost src]$ cd ../bin

    [developer @localhost bin]$ ./chapter1008_03

    The Administration id:1

    The Administration name:jack

    The Teacher id:2

    The Teacher name:john

    多重继承派生类拥有多个基类的所有成员,即派生类拥有多个基类的特性。上述实例中根据软件学院人员机构分析,有一类人员同时兼顾着行政人员与教师的特性,此时分析继承层次时可以将其定义为多重继承的方式。

    本实例中总共涉及三个类,Administrtion、Teacher与AdministrationTeacher分别表示行政人员类、教师类以及兼顾行政管理以及教师人员类。其中AdministrationTeacher类继承至Administrtion与Teacher类。

    而Administration类中除了构造与析构函数同时还包含两个公开的接口成员分别为,设置行政人员编号与设置行政人员的姓名的方法,另外还包含两个私有数据成员,分别表示行政人员的编号与姓名。而Teacher类中同样包含两个公开接口成员分别为,设置教师人员编号以及教师人员姓名的方法,同时也包含两个私有数据成员分别表示教师编号与姓名。最终继承至前两个类的AdministrationTeacher类在继承所有的方法成员之外,还包含该类添加的公开成员方法用于打印所设置的编号以及姓名信息。

    实例主程序中首先定义AdministrationTeacher类对象实例administrationTeacher,随后使用该对象实例调用其继承至基类的方法成员setAdministrationId、setAdministrationName、setTeacherId以及setTeacherName,并且根据传入实参设置该类中的保护型数据成员。由于基类中数据成员为保护类型,在继承类中依然保持保护类型的特性,那么最后对象实例administrationTeacher调用派生类自定义方法showAdministrationTeacherInfo,该方法中访问了基类中的保护成员,用于在屏幕上显示设置的成员信息。

    多重继承在C++应用软件开发中并不常用。因为其继承方式的使用不当会增加软件复杂出错的可能性,同时也可以会影响软件处理的效率。最常见的当多个基类中拥有同个方法成员,而这些基类被派生类继承的方式又不相同时,处理会变得复杂难懂。所以除非必须的处理场景下,一般不建议使用。

    展开全文
  • C++的继承

    2015-08-07 14:27:53
    10.1 C++继承简介 C++中作为一种自定义类型的数据结构,在面向对象程序设计思想中有着相当重要的作用。代表一事物的集合,与现实社会相似,类型同样具有继承实现的可能。...C++语言中将继承分为三种情

    10.1  C++类继承简介

    C++中类作为一种自定义类型的数据结构,在面向对象程序设计思想中有着相当重要的作用。类代表一类事物的集合,与现实社会相似,类类型同样具有继承实现的可能。现实社会中事物的继承性随处可见,如父子的继承关系等。

    C++软件开发中,继承机制有着很多的运用。通常在软件编程中,继承与多态性的结合运用可以增加软件的扩展性以及应用程序可重用性。C++语言中将继承分为三种情况,即publicprotected以及private继承。不同的继承方式有着不同的限制规则,这点会在后续的继承层次结构中详细讲述。

    通常,C++编程中的继承采用public方式居多,不排除特殊情况下会使用另外两种继承方式。但是protectedprivate继承方式的使用复杂性可能造成不必要的出错。下面主要通过类public继承方式来讲述类继承的基本概念与实际应用。首先通过一个完整的public继承方式的实例直观的了解继承的实现。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1001_01.hchapter1001_01.cppchapter1001_02.hchapter1001_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    /**

    * 实例chapter1001

    * 源文件chapter1001_01.h chapter1001_01.cpp

    * chapter1001_02.h chapter1001_02.cpp

    * 水果类单继承方式

    */

    //水果类头文件chapter1001_01.h

    #include <iostream>

    using namespace std;

     

    class Fruit                                                                           //水果类Fruit定义

    {

             public:                                                               //水果类公开接口成员

                       Fruit(){}                                                    //水果类构造函数定义

                       ~Fruit(){}                                                  //水果类析构函数定义

                       voidshowFruitInfo();                                      //水果类信息打印函数

    };

    //水果类源文件chapter1001_01.cpp

    #include "chapter1001_01.h"

     

    void Fruit::showFruitInfo()                                      //水果类信息打印方法外部定义

    {

             cout<<"Thisis Fruit class!"<<endl;

    }

    //苹果类头文件,chapter1001_02.h继承至水果类

    #include "chapter1001_01.h"

     

    class Apple: public Fruit                                         //苹果类Apple定义,以public方式继承至Fruit类

    {

             public:                                                               //苹果类公开接口成员

                       Apple(){}                                                 //苹果类构造函数定义

                       ~Apple(){}                                                        //苹果类析构函数定义

                       voidshowAppleInfo();                          //苹果类信息显示函数

    };

    //苹果类源文件chapter1001_02.cpp

    #include "chapter1001_02.h"

     

    void Apple::showAppleInfo()                                 //苹果类信息显示函数外部定义

    {

             cout<<"Thisis Apple class!"<<endl;

    }

     

    int main()

    {

             Fruitfruit;                                                          //主程序中定义水果类对象fruit

             Appleapple;                                                    //定义苹果类对象apple

             fruit.showFruitInfo();                                      //使用水果类对象调用其打印信息方法

             apple.showFruitInfo();                                   //使用苹果类对象调用继承至水果类方法成员打印类信息

             apple.showAppleInfo();                                //使用苹果类对象调用自身方法成员打印类信息

             return0;

    }

    本实例主要通过水果类、苹果类演示类单继承方式应用情况。程序主要由4个文件组成,分别为水果类头文件、源文件以及具体苹果类头文件、源文件。具体程序剖析见程序注释与后面讲解。

    2.编辑makefile

    Linux平台下需要编译源文件为chapter1001_01.cppchapter1001_02.cpp,相关makefile工程文件编译命令编辑如下所示。

    #makefile

    OBJECTS= chapter1001_01.o chapter1001_02.o

    CC=g++

    #生成可执行程序Apple

    Apple: $(OBJECTS)

             $(CC)$(OBJECTS) -o Apple

    chapter1001_01.o: chapter1001_01.h

    chapter1001_02.o: chapter1001_02.h

    #清除源文件以外的操作命令

    clean:

             rm-f Apple core $(OBJECTS)

    submit:

             cp-f -r Apple ../bin

             cp-f -r *.h ../include

    上述makefile文件的编写是Linux系统下工程化的编译思想的体现。由于Linux系统下编译器采用的是命令执行形式,makefile文件的组织应用,有利于大型软件工程中代码的编译管理。

    以上make命令是执行makefile文件中编译命令。为了使makefile编译文件显得更加的灵活可扩展,此处在makefile文件中增加变量定义,将makefile中多处重复使用的部分提取出来,采用变量定义来表示。这样下次需要增加或者修改对应的文件只需要修改一次即可。上述文件中,主要将程序生成的.o工程文件定义为变量OBJECTS表示,将编译器g++定义为变量CC表示。为此下次需要增加文件或者换用编译命令只需要修改该变量定义即可。

    随后将变量运用到执行命令中,此处,采用另一种形式来定义代码文件编译命令。直接通过代码工程文件以及对应的头文件,让make命令自动去推导程序应该执行的编译命令。此处主要涉及两个工程文件的编译,chapter1001_01.ochapter1001_02.o与其对应的代码文件分别为chapter1001_01.hchapter1001_01.cppchapter1001_02.h以及chapter1001_02.cpp,主程序入口定义于chapter1001_02.cpp中。最后,为了清除当前目录下生成的程序以及出错产生的core等垃圾文件定义了clean选项,选项下执行rm –f,表示指定清楚随后的各种文件。

    上述实例中不仅采用工程化makefile文件编译方式,同时也采用多代码文件的方式来编写实例程序。本实例共涉及四个代码文件,两个头文件代码,以及两个对应的代码源文件。在软件编程中,代码文件划分应用也是大型软件工程中必不可少的部分,头文件与源代码文件的区分会让软件组织结构更加的合理。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件。随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [developer @localhost src]$ make

    g++ -c -o chapter1001_01.o chapter1001_01.cpp

    g++ -c -o chapter1001_02.o chapter1001_02.cpp

    g++ chapter1001_01.o chapter1001_02.o  -o Apple

    [developer @localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [developer @localhost src]$ cd ../bin

    [developer @localhost bin]$ ./Apple

    This is Fruit class!

    This is Fruit class!

    This is Apple class!

    通常在C++面向对象的应用开发中,应用程序的头文件主要包含类体的定义,以及程序需要使用的常量定义等。而源文件则主要是类方法成员的具体实现的场所。使用时只需要#include包含对应的头文件即可。

    4.程序剖析

    多文件的使用更多的优势之处会在后续更多实例中慢慢体现出来,这里只作简单的介绍。本实例中主要涉及两个类,即水果类Fruit和苹果类Apple。两个类之间为继承关系,由于水果类代表一类广泛的概念类型,而苹果类则代表了具体水果的一种类型,它们之间存在一定的关联。这种关联可以理解为继承关联,即苹果类是从水果类继承而来的。

    实例中四个代码文件,其中chapter1001_01.hchapter1001_01.cpp为水果类定义文件。头文件chapter1001_01.h中包含类Fruit的定义,该类包含自定义空的构造函数与析构函数,以及一个打印自身信息的方法成员showFruitInfo(),暂不包括任何数据成员。chapter1001_01.cpp文件中主要定义实现类Fruit的方法成员,只包含chapter1001_01.h头文件,此处主要定义实现类方法成员showFruitInfo()

    头文件chapter1001_02.h中定义Apple类,采用公开继承方式从水果类派生定义而开。此时需要包含水果类头文件chapter1001_01.h,因为此处需要使用到该类的声明定义。该文件中Apple类公开继承至水果类Fruit,即意味着Apple类拥有了基类的所有成员,同时在此基础上Apple中还定义了显示本信息的方法成员,该成员在chapter1001_02.cpp源文件中定义实现。

    主程序中首先定义基类对象fruit,此时必然调用其定义构造函数构造该类对象实例。随后定义其派生类对象apple,该对象拥有基类一切成员的同时还拥有自定义的成员。程序中首先对Fruit对象实例构造完毕后,调用其信息显示函数showFruitInfo(),打印本类基本信息。随后使用苹果类对象实例分别调用自Fruit类继承的该方法,以及该类本身的信息显示方法。

    本实例仅仅演示类公开继承的方式的实现,此处仅仅涉及继承中的方法成员情况,下面将会就继承的更多具体操作作详细讲述。

    10.2  C++类继承层次结构

    C++中继承体系是一种类似数据结构中树状的层次结构,在树状层次的继承体系中,始终脱离不了两个基本概念即基类与派生类。通常C++软件开发中,类可以作为两种方式存在。一种是作为单独存在的自定义类型参与程序处理。另一种则是使用类继承机制,类通常不是作为顶层基类出现就是作为基类的派生类而存在,而该派生类又可以作为其派生类的基类。

    10.2.1  C++继承体系简介

    前面已经说明过,C++类一般可以作为继承顶层的基类,或者作为基类的派生类存在。基类通常又称为父类,在软件系统中基类应该表示一类事物最通用的基本特性。例如上小节中的水果与苹果类的继承关系,水果类代表一类水果最常见的特性与操作。而苹果类则为水果的一种,具有具体更多的特性描述,通过继承下次需要增加另外一种水果类型会变得很方便。直接继承至水果类即可实现,这种分析方式在面向对象中有助于理解使用继承增加软件可扩展性的概念。

    C++中类的继承体系可以是多个基类,派生类继承至该多个基类,通常这种继承方式称为为多继承。另外一种方式即可以是单个基类,扩展至多个派生类并且多个派生类之间是在同一个层次上,互不相干,同时派生类又可以继续派生下去。以下通过分析软件学院人员组织机构继承体系为例,如图10.1所示。

    10.1  软件学院机构简单继承体系图

    上述继承分析实例中最上层的基类表示软件学院人员。随后继承体系下出现并行同层次的单继承情况,即软件学院人员可以由行政人员、教师与学生成员组成。类Administration、类Teacher以及类Student直接继承至类SoftwareMember,这种继承方式称为单继承。

    而软件学院中通常有一种拥有双重身份的成员。如一名行政人员同时又是教师身份,此时该类需要同时继承行政类与教师类而来,表示该类人的双重身份。类AdministrationTeacher继承至类Administration与类Teacher,这种继承方式称为多继承。当然一个软件学院机构中成员不仅仅会涉及以上这些。本图仅仅突出整个机构中的小部分组织来演示继承体系中可能出现的情况,即单继承与多继承的概念。稍后会在后续小节中详细讲述。

    通常类继承机制的优势在于,现有不变的类类型基础之上创建新类,从而通过增加新类操作方法来达到添加新功能的目的。最常见的是库的应用开发中,开发者可以开发出一定范围内可重用的组件类。而这些组件类功能在应用时可以根据具体需要在不修改原类的基础上进行继承扩充。软件中类的继承层次分析方法应用很多,面向对象编程思想中掌握这种分层方法分析事物有助于设计出高可扩展性以及功能强大的软件系统。初学者可以通过分析现实世界中的一些继承实例来培养该方面的思维方式。

    10.2.2  单继承中的基类与派生类

    C++中单继承有着很广阔的运用,一般软件开发中通过继承现有功能类的方式设计出功能更强大的处理类。但是通常要求基类中封装一类事物中最普遍的属性与操作,供派生类继承并加入新的属性与操作进行扩展。下面依然通过水果类继承实例,加入操作方法与数据成员,了解继承体系中基类与派生类的使用。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1002_01.hchapter1002_01.cppchapter1002_02.hchapter1002_02.cpptestMain.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    /**

    * 实例chapter1002

    * 源文件chapter1002_01.hchapter1002_01.cpp

    * chapter1002_02.h chapter1002_02.cpp testMain.cpp

    * 水果类单继承方式

    */

    //水果基类头文件chapter1002_01.h

    #ifndef FRUIT_H

    #define FRUIT_H

    #include <iostream>

    #include <string>

    using namespace std;

     

    class Fruit                                                                                              //水果类Fruit

    {

             public:

                       Fruit();                                                                        //水果类构造函数

                       ~Fruit();                                                                      //水果类析构函数

                       voidsetFruitName(const char* name);               //水果类设置水果名称方法成员

                       voidshowFruitInfo();                                                        //水果类打印输出水果信息方法成员

                       voideatFruit();                                                          //水果类吃水果方法成员

             protected:

                       charm_name[100];                                                //水果类私有保护数据成员,表示水果名称

    };

    #endif

    //水果基类源文件chapter1002_01.cpp

    #include "chapter1002_01.h"

     

    Fruit::Fruit()                                                                                  //水果类构造函数定义实现

    {

             cout<<"Fruitclass construction!"<<endl;                      //打印输出构造函数信息

    }

    Fruit::~Fruit()                                                                                //水果类析构函数

    {

             cout<<"Fruitclass destruction!"<<endl;                        //打印输出析构函数信息

    }

    void Fruit::setFruitName(const char* name)                         //设置水果类名称信息方法定义实现

    {

             strcpy(m_name,name);                                                   //通过拷贝的方式将传入的参数赋值到数据成员中

    }

    void Fruit::showFruitInfo()                                                         //打印输出水果类信息方法定义实现

    {

             cout<<"showFruit member m_name:"<<m_name<<endl;        //打印输出水果类名称成员

    }

    void Fruit::eatFruit()                                                                    //水果类吃水果方法成员定义实现

    {

             cout<<"eatFruit!"<<endl;                                                 //打印输出水果类吃水果方法实现信息

    }

    //苹果类头文件chapter1002_02.h

    #include "chapter1002_01.h"

     

    class Apple: public Fruit                                                            //苹果类Apple,公开继承至水果基类Fruit

    {

             public:

                       Apple();                                                                      //苹果类构造函数

                       ~Apple();                                                                   //苹果类析构函数

                       voidsetAppleName(const char* name);            //苹果类设置名称方法成员

                       voidshowAppleInfo();                                            //苹果类打印苹果信息方法成员

                       voideatFruit();                                                          //苹果类中吃水果方法成员

    };

    //苹果类源文件chapter1002_02.cpp

    #include "chapter1002_02.h"

     

    Apple::Apple()                                                                             //苹果类构造函数定义实现

    {

             cout<<"Appleclass construction!"<<endl;

    }

    Apple::~Apple()                                                                                    //苹果类析构函数定义实现

    {

             cout<<"Appleclass destruction!"<<endl;

    }

    void Apple::setAppleName(const char* name)                   //苹果类设置名称方法定义实现

    {

             strcpy(m_name,name);

    }

    void Apple::showAppleInfo()                                                   //苹果类打印输出苹果信息方法成员定义实现

    {

             cout<<"showApple member m_name"<<m_name<<endl;

    }

    void Apple::eatFruit()                                                                           //苹果类中吃苹果方法成员定义实现

    {

             cout<<"eatApple!"<<endl;

    }

    //主程序源文件testMain.cpp

    #include "chapter1002_02.h"

    /*主函数入口*/

    int main()

    {

             Fruitfruit;                                                                            //定义水果类对象fruit

             fruit.setFruitName("Fruit");                                                     //水果类对象fruit调用其设置名称方法

             fruit.showFruitInfo();                                                         //水果类对象fruit调用其打印水果信息方法

             fruit.eatFruit();                                                                    //水果类对象fruit调用其吃水果方法

             Appleapple;                                                                      //定义苹果类对象apple

             apple.setAppleName("Apple");                                               //苹果类对象apple调用其设置名称方法

             apple.showAppleInfo();                                                   //苹果类对象apple调用其打印苹果信息方法

             apple.eatFruit();                                                                 //苹果类对象apple调用其吃水果方法

    }

    本实例程序主要通过水果、苹果类演示单类继承方式。程序主要由主函数与水果、苹果类组成,相关类中除了相应的构造与析构后,还包含相应输出数据成员信息的方法。程序具体分析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下需要编译源文件为chapter1002_01.cppchapter1002_02.cpptestMain.cpp,相关makefile工程文件编译命令编辑如下所示。

    OBJECTS=chapter1002_01.o chapter1002_02.otestMain.o

    CC=g++

     

    testMain: $(OBJECTS)

             $(CC)$(OBJECTS) -o testMain

    testMain.o:

    chapter1002_01.o: chapter1002_01.h

    chapter1002_02.o: chapter1002_02.h

     

    clean:

             rm-f testMain core $(OBJECTS)

    submit:

             cp-f -r testMain ../bin

             cp-f -r *.h ../include

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件。随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [billing@localhost src]$ make

    g++    -c -ochapter1002_01.o chapter1002_01.cpp

    g++    -c -ochapter1002_02.o chapter1002_02.cpp

    g++    -c -otestMain.o testMain.cpp

    g++ chapter1002_01.o chapter1002_02.o testMain.o-o testMain

    [billing@localhost src]$ make submit

    cp -f -r testMain ../bin

    cp -f -r *.h ../include

    [billing@localhost src]$ cd ../bin

    [billing@localhost bin]$ ./testMain

    Fruit class construction!

    show Fruit member m_name:Fruit

    eat Fruit!

    Fruit class construction!

    Apple class construction!

    show Apple member m_nameApple

    eat Apple!

    Apple class destruction!

    Fruit class destruction!

    Fruit class destruction!

    4.程序剖析

    本实例主要演示水果继承体系中单继承的基本实现。主要由两个类源文件组成包含水果基类以及其派生出的苹果类。苹果类采用public方式继承至基类水果类,其中基类中包含构造与析构函数、设置水果类名称方法、打印水果类信息方法以及吃水果动作方法接口的定义。派生出的苹果类除了拥有基类成员外根据需要添加了自身类的构造与析构函数、设置苹果类名称方法、打印苹果类信息方法以及吃水果动作方法接口定义。

    实例主程序源文件中,首先定义基类Fruit对象fruit。此时会调用其自定义构造函数构造对应的对象实例。构造函数中主要实现一条提示信息语句。随后通过该对象实例调用其方法成员setFruitName,根据传入的实参设置水果基类的保护数据成员。采用fruit对象调用showFruitInfo打印输出设置的水果基类的数据成员m_name。最后通过fruit对象调用其吃水果动作方法,该方法主要实现为打印执行该方法的提示信息。

    主程序中之后定义派生类苹果类对象实例apple,派生类由于继承基类所有成员。所以初始化构造派生类对象之前需要首先调用基类水果类的构造函数,即程序运行结果中首先会打印输出基类构造函数调用的提示信息。随后才调用派生苹果类构造信息。通过apple对象调用其setAppleName方法设置继承至基类水果类的保护数据成员,随后调用showAppleInfo方法打印输出该成员信息。最后则通过apple对象调用eatFruit方法打印输出属于吃苹果具体的信息提示。

    从上述实例中总结出单继承的一些特点。首先,单继承中基类对象定义操作与派生类无关,完全可以将基类作为一个单独的类来处理应用。其次派生类中通过public方式继承,包含了基类的所有的成员,其中保护数据成员m_name在派生类中拥有一份拷贝可以供其对象使用。另外派生类中对象实例构造时构造函数执行需要优先执行基类构造,同时对象实例退出时析构函数的调用顺序则正好相反。

    最后,派生类中允许定义与基类同样名称的方法成员。此时派生类对象调用同名称方法时,基类该方法成员将会被隐藏。上述实例中允许了同一个方法实现具体不同操作的作法,稍后下章会介绍虚函数在同样情况的应用情况处理。

    10.3  继承体系中的访问控制

    C++中单独类的访问控制在类章节中已经详细讲述过。但是由于继承拥有publicprotected以及private三种方式,针对不同的继承方式,与类中访问控制结合起来,使得派生类的访问控制变得更加的复杂,下面针对三类继承方式详细讲述派生类不同的访问控制策略。

    10.3.1  public继承

    类采用public方式从基类派生,基类中的publicprotected成员在派生类中依然保持publicprotected成员访问规则。类继承体系中,基类本身的访问控制方式与单独类访问控制无异,即可以将基类本身看成单独存在的类去应用,因为继承应用其中一个重要的原因就是基类可能被其它程序使用。为了保留基类对象的完整性,通过对基类的继承,来定义新的类类型供使用。这样在保持原有应用程序不变的情况下可以扩充和丰富具体继承类的操作。所以,基类在继承体系中依然保持独立的访问控制。

    public继承中,派生类继承至基类,即拥有基类的所有成员基础上增加属于派生类的成员。此时针对派生类的访问控制增加了基类成员部分。public继承方式下,基类的public成员在派生类中依然保持public属性,即派生类中可以保持对public成员的访问权限。派生类对象可以在类体外直接访问基类以及派生类本身的公开成员,包括非静态方法成员、友元函数以及数据成员。

    public继承方式下基类的protected成员在派生类中依然保持protected成员访问控制规则,即派生类中只有非静态方法成员以及友元可以直接访问。public继承方式下,基类的private成员在派生类中依然保持private成员特性,派生类中不允许访问基类的private成员。但是可以通过基类public或者protected成员来访问,为此保证基类的隐藏特性。下面通过一个完整实例演示公开继承访问控制方式。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1003_01.hchapter1003_01.cppchapter1003_02.hchapter1003_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    /**

    * 实例chapter1003

    * 源文件chapter1003_01.hchapter1003_01.cpp

    * chapter1003_02.h chapter1003_02.cpp

    * 水果类公开继承访问控制

    */

    //水果类头文件chapter1003_01.h

    #ifndef      FRUIT_H

    #define     FRUIT_H

    #include <iostream>

    #include <string>

    using namespace std;

     

    class Fruit                                                                                    //水果类Fruit定义

    {

             public:                                                                        //水果类公开方法成员

                       Fruit(){}                                                             //水果类构造函数定义

                       ~Fruit(){}                                                           //水果类析构函数定义

                       voidsetFruitId(int id);                                     //水果类设置水果编号方法成员

                       voidshowFruitId();                                         //水果类打印水果编号方法成员

             public:                                                                        //水果类公开数据成员

                       stringm_name;                                                       //字符串数据成员表示水果名称

             protected:                                                                  //水果类保护类型数据成员

                       intm_price;                                                     //整型数据成员表示水果价格

             private:                                                                       //水果类私有保护数据成员

                       intm_id;                                                           //整型数据成员表示水果编号

    };

    #endif

    //水果类源文件chapter1003_01.cpp

    #include "chapter1003_01.h"

     

    void Fruit::setFruitId(int id)                                                        //水果类公开设置水果编号函数定义,传入整型参数表示编号

    {

             m_id= id;

    }

    void Fruit::showFruitId()                                                   //水果类公开打印水果编号函数定义

    {

             cout<<"TheFruit id:"<<m_id<<endl;

    }

    //苹果类头文件chapter1003_02.h

    #ifndef APPLE_H

    #define APPLW_H

    #include "chapter1003_01.h"

     

    class Apple: public Fruit                                                  //公开继承至水果类的苹果类定义

    {

             public:                                                                        //苹果类公开方法成员

                       Apple(){}                                                           //苹果类构造函数

                       ~Apple(){}                                                                  //苹果类析构函数

                       voidsetApplePrice(int price);                       //苹果类设置苹果价格方法成员

                       voidshowApplePrice();                                 //苹果类打印苹果价格的方法成员

    };

    #endif

    //苹果类源文件chapter1003_02.cpp

    #include "chapter1003_02.h"

     

    void Apple::showApplePrice()                                        //苹果类打印苹果价格方法成员定义

    {

             cout<<"Thisis Apple price:"<<m_price<<endl;

    }

    void Apple::setApplePrice(int price)                              //苹果类设置苹果价格方法成员定义,传入整型参数表示价格

    {

             m_price= price;

    }

    int main()

    {

             Appleapple;                                                             //定义苹果类对象apple

             apple.m_name= "apple";                                               //对象apple访问继承至基类的公开数据成员m_name

             cout<<apple.m_name<<endl;                              //通过对象apple直接打印类公开成员m_name

             apple.setApplePrice(3);                                         //apple对象调用设置苹果价格方法

             apple.showApplePrice();                                       //apple对象调用打印苹果价格方法

             apple.setFruitId(1);                                                  //apple对象调用设置水果编号方法

             apple.showFruitId();                                                //apple对象调用打印水果编号方法

             return0;

    }

    本实例通过水果类单继承方式演示类继承中公共继承访问控制情况。程序由主函数与水果、苹果类组成。具体程序剖析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下其makefile编译文件中涉及两个头文件以及两个源代码文件,makefile文件编辑如下。

    OBJECTS= chapter1003_01.o chapter1003_02.o

    CC=g++

     

    Apple: $(OBJECTS)

             $(CC)$(OBJECTS) -o Apple

    chapter1003_01.o: chapter1003_01.h

    chapter1003_02.o: chapter1003_0.h

     

    clean:

             rm-f Apple core $(OBJECTS)

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [developer @localhost src]$ make

    g++    -c -ochapter1003_01.o chapter1003_01.cpp

    g++    -c -ochapter1003_02.o chapter1003_02.cpp

    g++ chapter1003_01.o chapter1003_02.o -o Apple

    [developer @localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [developer @localhost src]$ cd ../bin

    [developer @localhost bin]$ ./Apple

    apple

    This is Apple price:3

    The Fruit id:1

    4.程序剖析

    上述实例依然采用水果继承实例作为演示。基类水果类中定义三类不同权限控制成员,分别是公开接口成员,以及公开数据成员、保护数据成员与私有数据成员。实例目的是验证在其派生类Apple类中访问基类成员的控制规则。Fruit基类中公开接口分别为setFruitId()showFruitId()两个方法成员。该方法成员主要访问其私有保护数据成员,设置以及打印该该保护成员表示的水果编号。

    派生而来的Apple类中除了继承至Fruit类所有成员之外,同时添加属于Apple类的方法成员setApplePrice()showApplePrice()。分别用来在Apple类中设置、打印继承到的保护数据成m_price。主函数中首先定义Apple类对象。由于公有继承原先基类的公开成员,在派生类中非静态函数、友元以及派生类外部函数中可以直接使用,所以随后直接采用Apple类对象实例调用其继承基类的数据成员m_name并直接赋值,最后打印输出。

    Apple对象实例apple调用设置苹果价格的方法成员。根据传入的实参来设置继承至基类保护类型数据成员,由于公开继承中保护成员在派生类中依然保持保护成员的访问控制特性。所以在派生类中自然可以通过其public类型方法成员操作。Apple对象实例调用完价格成员设置之后,调用其价格打印方法成员在屏幕上打印显示价格信息。

    由于public类型继承方式,私有的成员在派生类中都不可以直接操作。所以只能通过继承至基类的公开接口来操作其私有保护成员。上述实例中Apple类对象实例调用继承至Fruit类的公开方法成员setFruitId以及showFruitId方法,分别设置该基类私有数据成员编号值以及屏幕打印该值。

    10.3.2  protected继承

    当派生类采用protected方式继承至基类时,基类中的publicprotected成员在派生类中都自动转变为protected成员。即此时只能通过派生类的非静态成员函数以及其友元函数可直接访问。而基类的私有保护成员在派生类中则被隐藏,只能通过调用基类的公开成员以及保护成员来访问。

    为了演示上述继承方式下成员访问控制,则上一小节实例可修改为protected继承方式。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1004_01.hchapter1004_01.cppchapter1004_02.hchapter1004_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    //水果类Fruit.h

    #ifndef FRUIT_H

    #define FRUIT_H

    #include <iostream>

    #include <string>

    using namespace std;

     

    class Fruit

    {

               public:

                      Fruit(){}

                      ~Fruit(){}

                      void setFruitId(int id);

                      void setFruitName(string name);

                      void showFruitId();

                      void showFruitName();

               public:

                      string m_name;

               protected:

                      int m_price;

               private:

                      int m_id;

    };

    #endif

    //水果类源文件Fruit.cpp

    #include "Fruit.h"

     

    void Fruit::setFruitId(int id)

    {

               m_id = id;

    }

    void Fruit::setFruitName(string name)

    {

               m_name = name;

    }

    void Fruit::showFruitId()

    {

               cout<<"The Fruitid:"<<m_id<<endl;

    }

    void Fruit::showFruitName()

    {

               cout<<"The Fruitname:"<<m_name<<endl;

    }

    //苹果类头文件Apple.h

    #ifndef APPLE_H

    #define APPLW_H

    #include "Fruit.h"

     

    class Apple: protected Fruit

    {

               public:

                     Apple(){}

                     ~Apple(){}

                     void setAppleInfo(int id,string name,intprice);

                     void showAppleInfo();

    };

    #endif

    //苹果类源文件Apple.cpp

    #include "Apple.h"

     

    void Apple::showAppleInfo()

    {

               showFruitId();

               showFruitName();

               cout<<"This is Appleprice:"<<m_price<<endl;

    }

    void Apple::setAppleInfo(int id,string name,intprice)

    {

               m_price = price;

               setFruitId(id);

               setFruitName(name);

    }

     

    int main()

    {

               Apple apple;

               apple.setAppleInfo(1,"Apple",3);

               apple.showAppleInfo();

               return 0;

    }

    本实例通过水果类单继承方式演示类继承中保护继承访问控制情况。程序由主函数与水果、苹果类组成。具体程序剖析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下其makefile编译文件中涉及两个头文件以及两个源代码文件,makefile文件编辑如下。

    OBJECTS=chapter1004_01.o chapter1004_02.o

    CC=g++

     

    Apple: $(OBJECTS)

             $(CC)$(OBJECTS) -o Apple

    chapter1004_01.o: chapter1004_01.h

    chapter1004_02.o: chapter1004_02.h

     

    clean:

             rm-f Apple core $(OBJECTS)

    submit:

             cp-f -r Apple ../bin

             cp-f -r *.h ../include

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [billing@localhost src]$ make

    g++    -c -ochapter1004_01.o chapter1004_01.cpp

    g++    -c -ochapter1004_02.o chapter1004_02.cpp

    g++ chapter1004_01.o chapter1004_02.o -o Apple

    [billing@localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [billing@localhost src]$ cd ../bin

    [billing@localhost bin]$ ./Apple

    The Fruit id:1

    The Fruit name:Apple

    This is Apple price:3

    本实例程序中,采用protected继承方式。由于基类中publicprotected成员在派生类中统一演变为protected成员,所以基类中定义的setFruitIdsetFruitNameshowFruitId、与showFruitName方法成员在派生类中只能通过派生类自定义方法成员访问。所以派生类中增加setAppleInfoshowAppleInfo方法用于调用基类中相关公开接口成员,便于外界通过派生类对象间接调用。

    与上述实例不同的是,主程序中,基类public类型的数据成员m_name在派生类中已经变为protected成员,不能在派生类外部函数中直接访问。同时,基类公开的public成员也不能在派生类外部通过对象实例直接调用,而只能通过派生成员间接访问其基类公开成员。另外,基类中的私有数据成员也只能通过基类publicprotected成员访问,protected继承方式实际上增加了了继承类中成员的访问控制力度。

    10.3.3  private继承

    private继承方式则更加严格控制成员访问限度。基类的public成员在派生类中演变为private类型只能通过其非静态成员函数以及友元访问。另外,基类的protected成员在派生类中也演变为private私有类型,访问规则同上。基类中的private成员则与前面两种继承方式相同,派生类中此类数据被直接隐藏,只能通过基类公开以及保护方法成员去访问。

    类中private继承方式实例初学者可以根据前两个继承方式来修改后,执行验证其运行结果。至此类的三种继承方式介绍完毕,初学者可以通过自定义实例来验证不同种情况下类成员的访问控制规则,不断的提高自身对于继承体系概念的认识。

    10.4  继承体系中的构造与析构

    C++中继承概念前面基本已经有一个大致的轮廓。但是其中还涉及到一个重要的部分,即基类、派生类的构造函数与析构函数定义执行的情况,下面将会就该主题通过完整实例作详细讲述。

    10.4.1  派生类构造函数

    继承体系中对象实例的构造有着一定的执行顺序,主要针对派生类对象实例构造,基类构造以及派生类构造函数的执行情况作详细讲述。下面通过一个派生类对象构造实例作为开端。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1005_01.hchapter1005_01.cppchapter1005_02.hchapter1005_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    //水果类头文件chapter1005_01.h

    #include <iostream>

    using namespace std;

     

    class Fruit

    {

             public:

                       Fruit();

                       voidshowFruitInfo();

    };

    //水果类源文件chapter1005_01.cpp

    #include "chapter1005_01.h"

     

    Fruit::Fruit()

    {

             cout<<"Fruitclass construction!"<<endl;

    }

    void Fruit::showFruitInfo()

    {

             cout<<"Thisis Fruit class!"<<endl;

    }

    //苹果类头文件chapter1005_02.h

    #include "chapter1005_01.h"

     

    class Apple: public Fruit

    {

             public:

                       Apple();

                       ~Apple(){}

    };

    //苹果类源文件chapter1005_02.cpp

    #include "chapter1005_02.h"

     

    Apple::Apple()

    {

             cout<<"Appleclass construction!"<<endl;

    }

     

    int main()

    {

             Fruitfruit;

             Appleapple;

             apple.showFruitInfo();

             return0;

    }

    本实例主要通过水果类继承方式演示继承体系中构造函数执行顺序情况。程序主要由主函数与水果、苹果类组成。程序具体剖析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下需要编译源文件为chapter1005_01.cppchapter1005_02.cpp,相关makefile工程文件编译命令编辑如下所示。

    OBJECTS=chapter1005_01.o chapter1005_02.o

    CC=g++

     

    Apple: $(OBJECTS)

             $(CC)$(OBJECTS) -o Apple

    chapter1005_01.o: chapter1005_01.h

    chapter1005_02.o: chapter1005_02.h

     

    clean:

             rm-f Apple core $(OBJECTS)

    submit:

             cp-f -r Apple ../bin

             cp-f -r *.h ../include

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [billing@localhost src]$ make

    g++    -c -ochapter1005_01.o chapter1005_01.cpp

    g++    -c -ochapter1005_02.o chapter1005_02.cpp

    g++ chapter1005_01.o chapter1005_02.o -o Apple

    [billing@localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [billing@localhost src]$ cd ../bin

    [billing@localhost bin]$ ./Apple

    Fruit class construction!

    Fruit class construction!

    Apple class construction!

    This is Fruit class!

    本实例共涉及两个类,即水果类与其派生类苹果类。其中水果类除了包含有自身的构造函数外还拥有一个打印本身类信息成员方法。其派生类Apple中,只拥有属于自身一个构造函数定义,另外就是继承至水果类的打印方法成员。

    主程序中首先定义水果类对象实例,此时根据类构造规则如果没有自定义构造函数,则调用默认构造来构造对象实例。而此处基类自定义了构造函数,则调用自定义构造函数,该函数中执行体只有打印其执行构造的信息“Fruit class construction!”。

    随后定义派生类对象,此时注意到,派生类对象实例构造会同时调用基类构造函数与派生类自身的构造函数,并且基类构造函数首先被执行,所以会先调用基类构造函数打印“Fruit class construction!”随后调用派生类构造函数打印信息“Apple class construction!”。最终调用继承至基类的打印类信息方法打印信息“This is Fruit class”从而结束程序运行。

    从上述实例表明,基类对象构造时保持类构造特性,而派生类对象实例构造时会优先调用基类构造函数。原因在于派生类中继承有基类的成员,派生类对象构造时需要优先构造初始化基类成员以供派生类使用。派生类对象实例构造时,优先处理基类构造函数,如果基类没有自定义构造函数则直接调用其默认构造即可。另外基类的构造函数可以由派生类单独显式调用,上述实例修改如下所示。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1006_01.hchapter1006_01.cppchapter1006_02.hchapter1006_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    //水果类头文件chapter1006_01.h

    #include <iostream>

    using namespace std;

     

    class Fruit                                                                                              //水果类Fruit定义

    {

             public:

                       Fruit(intid,string name);                                         //水果类构造函数,两个整型参数

             protected:

                       intm_id;                                                                    //水果类保护数据成员,表示水果编号

                       stringm_name;                                                                 //水果类保护数据成员,表示水果名称

    };

    //水果类源文件chapter1006_01.cpp

    #include "chapter1006_01.h"

     

    Fruit::Fruit(int id,string name)                                                   //水果类构造函数定义

    {

             m_id= id;

             m_name= name;

             cout<<"Fruitclass construction!"<<endl;

    }

    //苹果类头文件chapter1006_02.h

    #include "chapter1006_01.h"

     

    class Apple: public Fruit                                                            //继承至水果类的苹果类Apple定义

    {

             public:

                       Apple(intid,string name,int price);                       //苹果类构造函数

                       ~Apple(){}                                                                           //苹果类析构函数

                       voidshowAppleInfo();                                            //打印苹果类信息函数

             protected:

                       intm_price;                                                               //苹果类保护数据成员,表示苹果价格

    };

    //苹果类源文件chapter1006_02.cpp

    #include "chapter1006_02.h"

     

    Apple::Apple(int id,string name,int price)                              //苹果类构造函数定义

             :Fruit(id,name)

    {

             m_price= price;

             cout<<"Appleclass construction!"<<endl;

    }

    void Apple::showAppleInfo()                                                   //打印苹果类函数定义

    {

             cout<<"Fruitid:"<<m_id<<endl;

             cout<<"Fruitname:"<<m_name<<endl;

             cout<<"Fruitprice:"<<m_price<<endl;

    }

     

    int main()

    {

             Fruitfruit(1,"Fruit");                                                            //定义水果类对象实例,同时传入实参初始化

             Appleapple(2,"Apple",3);                                                //定义苹果类对象实例,同时传入实参初始化

             apple.showAppleInfo();                                                   //调用apple对象实例打印方法显示信息

             return0;

    }

    本实例主要通过水果类继承方式演示继承体系中构造函数执行顺序情况。程序主要由主函数与水果、苹果类组成。程序具体剖析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下其makefile编译文件中涉及两个头文件以及两个源代码文件,makefile文件编辑如下。

    OBJECTS=Fruit.o Apple.o

    CC=g++

     

    Apple: $(OBJECTS)

             $(CC)$(OBJECTS) -o Apple

    Fruit.o: Fruit.h

    Apple.o: Apple.h

     

    clean:

             rm-f Apple core $(OBJECTS)

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [developer @localhost src]$ make

    g++    -c -ochapter1006_01.o chapter1006_01.cpp

    g++    -c -ochapter1006_02.o chapter1006_02.cpp

    g++ chapter1006_01.o chapter1006_02.o -o Apple

    [developer @localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [developer @localhost src]$ cd ../bin

    [developer @localhost bin]$ ./Apple

    Fruit class construction!

    Fruit class construction!

    Apple class construction!

    Fruit id:2

    Fruit name:Apple

    Fruit price:3

    经过修改的实例中主要增加了基类的保护型数据成员以及自定义构造函数。其中,构造函数涉及两个基类成员的初始化工作。两个数据成员m_idm_name分别表示水果类的编号与名称,构造函数定义中主要依据外界传入参数来初始化该数据成员。派生类中则除了继承至基类的保护型数据成员外,增加一个m_price数据成员表示水果的价格,同样其构造函数中实现根据参数传递初始化该值。

    所不同的是,在派生类Apple构造函数定义中,增加了显示调用基类构造函数的操作,通过“:”初始化参数列表的方式来调用基类的构造函数。同时将根据调用派生类构造函数传入的实参来作为基类构造函数实参初始化。主程序中首先定义Fruit类对象实例fruit,同时传入编号以及名称值构造并初始化对象实例。随后定义派生类对象apple,此时传入三个实参用来构造初始化,其中,前两个参数用于显式调用基类构造函数作为实参传入并初始化。最后在派生类中增加打印基类以及派生类所有保护数据成员信息,验证构造初始化工作的正确性。

    综上所述,继承体系中派生类构造对象实例时。首先调用基类的构造函数用于初始化基类成员。当基类没有自定义构造函数时,调用其默认构造函数。同时基类有多个构造函数重载时也可以在派生类中显式的指定调用基类哪个构造函数来构造对象实例。

    10.4.2  派生类析构函数

    C++类继承体系中析构函数的操作次序与其构造函数正巧相反。此时派生类的析构函数调用要在基类析构函数调用之前发生。尤其当基类中包含有对象成员时,通常是先调用对象成员构造函数后调用基类构造函数,最后调用派生类构造函数,而析构函数调用顺序刚好相反。修改上一小节实例增加析构函数简单说明继承中析构函数使用情况,修改后的代码如下所示。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1007_01.hchapter1007_01.cppchapter1007_02.hchapter1007_02.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    //水果类头文件chapter1007_01.h

    #include <iostream>

    using namespace std;

     

    class Fruit

    {

             public:

                       Fruit(intid,string name);

                       ~Fruit();

             protected:

                       intm_id;

                       stringm_name;

    };

    //水果类源文件chapter1007_01.cpp

    #include "chapter1007_01.h"

     

    Fruit::Fruit(int id,string name)

    {

             m_id= id;

             m_name= name;

             cout<<"Fruitclass construction!"<<endl;

    }

    Fruit::~Fruit()

    {

             cout<<"Fruitclass destruction!"<<endl;

    }

    //苹果头文件chapter1007_02.h

    #include "chapter1007_01.h"

     

    class Apple: public Fruit

    {

             public:

                       Apple(intid,string name,int price);

                       ~Apple();

                       voidshowAppleInfo();

             protected:

                       intm_price;

    };

    //苹果类源文件chapter1007_02.cpp

    #include "chapter1007_02.h"

     

    Apple::Apple(int id,string name,int price)

             :Fruit(id,name)

    {

             m_price= price;

             cout<<"Appleclass construction!"<<endl;

    }

    Apple::~Apple()

    {

             cout<<"Appleclass destruction!"<<endl;

    }

    void Apple::showAppleInfo()

    {

             cout<<"Fruitid:"<<m_id<<endl;

             cout<<"Fruitname:"<<m_name<<endl;

             cout<<"Fruitprice:"<<m_price<<endl;

    }

     

    int main()

    {

             Fruit fruit(1,"Fruit");

             Apple apple(2,"Apple",3);

             apple.showAppleInfo();

             return 0;

    }

    本实例主要通过水果类继承方式演示继承体系中析构函数执行顺序情况。程序主要由主函数与水果、苹果类组成。程序具体剖析见程序注释与后面的讲解。

    2.编辑makefile

    Linux平台下其makefile编译文件中涉及两个头文件以及两个源代码文件,makefile文件编辑如下。

    OBJECTS=chapter1007_01.o chapter1007_02.o

    CC=g++

     

    Apple: $(OBJECTS)

             $(CC)$(OBJECTS) -o Apple

    chapter1007_01.o: chapter1007_01.h

    chapter1007_02.o: chapter1007_02.h

     

    clean:

             rm-f Apple core $(OBJECTS)

    submit:

             cp-f -r Apple ../bin

             cp-f -r *.h ../include

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [developer @localhost src]$ make

    g++    -c -ochapter1007_01.o chapter1007_01.cpp

    g++    -c -ochapter1007_02.o chapter1007_02.cpp

    g++ chapter1007_01.o chapter1007_02.o -o Apple

    [developer @localhost src]$ make submit

    cp -f -r Apple ../bin

    cp -f -r *.h ../include

    [developer @localhost src]$ cd ../bin

    [developer @localhost bin]$ ./Apple

    Fruit class construction!

    Fruit class construction!

    Apple class construction!

    Fruit id:2

    Fruit name:Apple

    Fruit price:3

    Apple class destruction!

    Fruit class destruction!

    Fruit class destruction!

    与上小节运行结果不同的是,本实例增加自定义析构函数中提示信息。因此在主程序中对象生命周期结束后,优先调用其派生类析构函数打印信息“Apple class Destruction!”。随后调用基类析构函数析构方法,最后因为基类对象实例最先被定义,所以基类析构函数最后被执行。

    继承体系中构造析构函数的操作情况。建议初学者通过多写一些不同情况下处理构造析构的程序来验证总结继承体系中构造与析构执行顺序以及应用处理。

    10.5  多重继承

    C++中继承除了从单个基类派生以外还可以从多个基类派生继承创建新类。这种继承方式称为多重继承,下面将会详细讲述多重继承的基本应用情况。

    所谓多重继承,即派生类继承至多个基类,并且拥有其多个基类的成员。多重继承方式与单继承方式基本语法实现形式相同,仅仅是在继承实现时通过逗号分隔符隔开多个基类名,其一般形式如下。

    派生类: public/protected/private 基类1,基类2…

    派生类继承至多个基类,即派生类中拥有多个基类成员,当然基类的构造函数与析构函数除外。多重继承除了在定义形式上与单继承稍有不同,其余访问控制等操作运用基本与单继承一致。下面通过一个完整实例演示前面小节分析的软件学院机构人员继承层次,其中行政人员同时又可以是老师的身份部分就包括多重继承的运用,即该类同时具有行政人员类与教师类的特征。

    1.准备实例

    打开UE工具,创建新的空文件并且另存为chapter1008_01.hchapter1008_01.cppchapter1008_02.hchapter1008_02.cppchapter1008_03.hchapter1008_03.cpp。该代码文件随后会同makefile文件一起通过FTP工具传输至Linux服务器端,客户端通过scrt工具访问操作。程序代码文件编辑如下所示。

    //行政人员类头文件chapter1008_01.h

    #ifndef ADMINISTRATION_H

    #define ADMINISTRATION_H

    #include <iostream>

    #include <string>

    using namespace std;

     

    class Administration

    {

             public:

                       Administration(){}

                       ~Administration(){}

                       voidsetAdministrationId(int id);

                       voidsetAdministrationName(string name);

             protected:

                       intm_AdministrationId;

                       stringm_AdministrationName;

    };

    #endif

    //行政人员类源文件chapter1008_01.cpp

    #include "chapter1008_01.h"

     

    void Administration::setAdministrationId(int id)

    {

             m_AdministrationId= id;

    }

    void Administration::setAdministrationName(stringname)

    {

             m_AdministrationName= name;

    }

    //教师类头文件chapter1008_02.h

    #ifndef TEACHER_H

    #define TEACHER_H

    #include <iostream>

    #include <string>

    using namespace std;

     

    class Teacher

    {

             public:

                       Teacher(){}

                       ~Teacher(){}

                       voidsetTeacherId(int id);

                       voidsetTeacherName(string name);

             protected:

                       intm_teacherId;

                       stringm_teacherName;

    };

    #endif

    //教师类源文件chapter1008_02.cpp

    #include "chapter1008_02.h"

     

    void Teacher::setTeacherId(int id)

    {

             m_teacherId= id;

    }

    void Teacher::setTeacherName(string name)

    {

             m_teacherName= name;

    }

    //行政管理同时又是教师身份人员类头文件chapter1008_03.h

    #ifndef ADMINISTRATIONTEACHER_H

    #define ADMINISTRATIONTEACHER_H

    #include "chapter1008_02.h"

    #include "chapter1008_01.h"

     

    class AdministrationTeacher:publicAdministration,public Teacher

    {

             public:

                       AdministrationTeacher(){}

                       ~AdministrationTeacher(){}

                       voidshowAdministrationTeacherInfo();

    };

    #endif

    //行政人员同时又是教师身份人员类的源文件chapter1008_03.cpp

    #include "chapter1008_03.h"

     

    voidAdministrationTeacher::showAdministrationTeacherInfo()

    {

             cout<<"TheAdministration id:"<<m_AdministrationId<<endl;

             cout<<"TheAdministration name:"<<m_AdministrationName<<endl;

             cout<<"TheTeacher id:"<<m_teacherId<<endl;

             cout<<"TheTeacher name:"<<m_teacherName<<endl;

    }

    int main()

    {

             AdministrationTeacheradministrationTeacher;

             administrationTeacher.setAdministrationId(1);

             administrationTeacher.setAdministrationName("jack");

             administrationTeacher.setTeacherId(2);

             administrationTeacher.setTeacherName("john");

             administrationTeacher.showAdministrationTeacherInfo();

             return0;

    }

    本实例程序主要通过教师类继承体系演示了多重继承的功能。程序主要由主函数与教师继承类组成,教师继承类体系中包含学校行政人员类、教师类以及两者身份兼有的类。程序具体剖析见程序注释与后面讲解。

    2.编辑makefile

    Linux平台下其makefile编译文件中涉及三个头文件以及三个源代码文件,makefile文件编辑如下。

    OBJECTS=chapter1008_01.o chapter1008_02.ochapter1008_03.o

    CC=g++

     

    chapter1008_03: $(OBJECTS)

             $(CC)$(OBJECTS) -o chapter1008_03

    chapter1008_01.o: chapter1008_01.h

    chapter1008_02.o: chapter1008_02.h

    chapter1008_03.o: chapter1008_03.h

     

    clean:

             rm-f chapter1008_03 core $(OBJECTS)

    submit:

             cp-f -r chapter1008_03 ../bin

             cp-f -r *.h ../include

    上述makefile文件套用前面的模板格式,主要替换了代码文件、程序编译中间文件、可执行程序等。在编译命令部分-g选项的加入,表明程序编译同时加入了可调式信息。

    3.编译运行程序

    当前shell下执行make命令,生成可执行程序文件,随后通过make submit命令提交程序文件至本实例bin目录,通过cd命令定位至实例bin目录,执行该程序文件运行结果如下所示。

    [developer @localhost src]$ make

    g++    -c -ochapter1008_01.o chapter1008_01.cpp

    g++    -c -ochapter1008_02.o chapter1008_02.cpp

    g++    -c -o chapter1008_03.o chapter1008_03.cpp

    g++ chapter1008_01.o chapter1008_02.ochapter1008_03.o -o chapter1008_03

    [developer @localhost src]$ make submit

    cp -f -r chapter1008_03 ../bin

    cp -f -r *.h ../include

    [developer @localhost src]$ cd ../bin

    [developer @localhost bin]$ ./chapter1008_03

    The Administration id:1

    The Administration name:jack

    The Teacher id:2

    The Teacher name:john

    多重继承派生类拥有多个基类的所有成员,即派生类拥有多个基类的特性。上述实例中根据软件学院人员机构分析,有一类人员同时兼顾着行政人员与教师的特性,此时分析继承层次时可以将其定义为多重继承的方式。

    本实例中总共涉及三个类,AdministrtionTeacherAdministrationTeacher分别表示行政人员类、教师类以及兼顾行政管理以及教师人员类。其中AdministrationTeacher类继承至AdministrtionTeacher类。

    Administration类中除了构造与析构函数同时还包含两个公开的接口成员分别为,设置行政人员编号与设置行政人员的姓名的方法,另外还包含两个私有数据成员,分别表示行政人员的编号与姓名。而Teacher类中同样包含两个公开接口成员分别为,设置教师人员编号以及教师人员姓名的方法,同时也包含两个私有数据成员分别表示教师编号与姓名。最终继承至前两个类的AdministrationTeacher类在继承所有的方法成员之外,还包含该类添加的公开成员方法用于打印所设置的编号以及姓名信息。

    实例主程序中首先定义AdministrationTeacher类对象实例administrationTeacher,随后使用该对象实例调用其继承至基类的方法成员setAdministrationIdsetAdministrationNamesetTeacherId以及setTeacherName,并且根据传入实参设置该类中的保护型数据成员。由于基类中数据成员为保护类型,在继承类中依然保持保护类型的特性,那么最后对象实例administrationTeacher调用派生类自定义方法showAdministrationTeacherInfo,该方法中访问了基类中的保护成员,用于在屏幕上显示设置的成员信息。

    多重继承在C++应用软件开发中并不常用。因为其继承方式的使用不当会增加软件复杂出错的可能性,同时也可以会影响软件处理的效率。最常见的当多个基类中拥有同个方法成员,而这些基类被派生类继承的方式又不相同时,处理会变得复杂难懂。所以除非必须的处理场景下,一般不建议使用。

    展开全文
  • linux 中C++的继承

    千次阅读 2016-10-24 19:35:54
    10.1 C++继承简介 C++中作为一种自定义类型的数据结构,在面向对象程序设计思想中有着相当重要的作用。代表一事物的集合,与现实社会相似,类型同样具有继承实现的...C++语言中将继承分为三种情况,即pu
  • 通常我们根据不同的结构特性将数组分为三类:一维数组,二维数组和多维数组(存储(n-1)维数组的数组,不做过多讨论) 本节中我们主要针对一维数组进行详细讲解,分析数组的应用场景,使用方法及其优缺点。 一维...
  • 这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐个男朋友送COMMAND,就数你最小气,才请我吃面。”       15. INTERPRETER 解释器模式   ...
  • 8. 数据结构按逻辑结构可分为两大,它们分别是 线性结构 和 非线性结构 。 9. 线性结构中元素之间存在 一对一线性 关系,树形结构中元素之间存在一对多层次 关系,图形结构中元素之间存在 多对多任意 关系。 10. ...
  • 数据信息可分为数值信息和非数值信息两。 1-8 什么叫二进制?使用二进制有何优点和缺点? 解: 二进制是基数为2,每位的权是以2 为底的幂的进制,遵循逢二进一原则,基本符号为0和1。采用二进制码表示信息,有...
  • Unix/Linux C++应用开发-C++的继承

    万次阅读 2013-09-11 21:07:30
    C++中作为一种自定义类型的数据结构,在面向对象程序设计思想中有着相当重要的作用。代表一事物的集合,与现实社会相似,类型同样具有继承实现的可能。...C++语言中将继承分为三种情况,即
  • 数据结构 1800题》

    热门讨论 2012-12-27 16:52:03
    7.从逻辑上可以把数据结构分为(C )两大。【武汉交通科技大学 1996 一 、4(2分)】 A.动态结构、静态结构 B.顺序结构、链式结构 C.线性结构、非线性结构 D.初等结构、构造型结构 8.以下与数据的存储...
  • 实际上,这三类算法本身仍然以模版形式实现,包括边分类、达分量、连通分量、最 短路径以及最小生成树在内的各种具体算法,都进而基于这三个模版分别得到了实现。 书中涉及的所有代码,都符合 J2SDK-1.4.1 规范,...
  • Java数据编程指南

    2008-07-24 16:17:34
    附录 附录A SQL入门 什么是SQL 什么是数据库 关系数据库基础 关系数据库的优点和缺点 SQL:数据定义 基本数据操作 数据完整性 表达式 连接 合并 子查询 小结 附录B 序列...
  • 数据结构(C++)有关练习题

    热门讨论 2008-01-02 11:27:18
    内容及步骤: 1、 设计一个图的,采用临接表法进行存储,该图每个结点的数据类型模板的模板参数进行定义(注:需先设计一个结点Node); 2、 为该分别设计一个实现深度优先搜索和广度优先搜索的成员...
  • 数据结构是指相互直接存在一种或多种特殊关系数据元素的集合。通常情况下,精心选择数据结构可以带来更高的运行或者存储效率。作为一名程序猿,更需要了解下数据结构。AND WHY?可以参考这篇...
  • Oracle基础知识

    2015-01-13 23:30:15
    小型数据库:access/foxbase 中型数据库:MySql/Sql server/ 大型数据库:Oracle(DBA认证就是数据库管理员)/Sybase/...关系数据库的查询可分为:1、关系数据语言2、关系演算语言 五中关系代数:1)、并(union)2)
  • Java基础十一

    2016-10-29 12:46:27
     答:由关系数据数据之间的约束者所组成的数据模型则称为RDBMS,即关系型数据库管理系统。  问:简述结构化查询语言的分类  答:SQL(Structured Query Language)结构化查询语言  SQL可分为:  -数据...
  • SQL 基础笔记(一)

    2018-06-15 16:27:00
    本笔记整理自《SQL 基础教程》、《MySQL 必知必会》和网上资料。...SQL 根据功能的不同,可分为三类(其中DML用得最多,增删查改嘛) DDL(Data Definition Language, 数据定义语言): CREATE/DR...
  • 本项目结构上分为数据访问层、业务逻辑层和表现层,层次间的依赖关系自下到上。采用的技术有spring,springMVC,Mybatis,Thymeleaf等。其中表现层使用springMVC框架开发;业务逻辑层为了增加程序的扩展和可维护性...
  • 而在当今的互联网中,通常把数据库分为,即关系型数据库和非关系型数据库。关系型数据库关系型数据库是指采用了关系模型来组织数据的数据库,而关系模型是由二维表及其联系组成的数据组织。优点:1、易于维护:...
  • 按照功能又可分为四大; DQL 查询语言,基本语句 SELECT; DML 操纵语言,主要有种形式,INSERT、UPDATE 和 DELETE; DDL 定义语言,创建表、视图、索引等,CREATE TABLE; DCL 控制语言,用来授权或...
  •   <br/>本书将全面介绍C#编程的所有知识,共分为5篇:第1篇是C#语言:介绍了C#语言的所有内容,从基础知识到面向对象的技术,应有尽有。第2篇是Windows编程:介绍如何用C#编写Windows应用程序,如何部署它们。...
  • 而在当今的互联网中,通常把数据库分为,即关系型数据库和非关系型数据库。 关系型数据库 关系型数据库是指采用了关系模型来组织数据的数据库,而关系模型是由二维表及其联系组成的数据组织。 优点: 1、易于...

空空如也

空空如也

1 2 3 4 5 ... 15
收藏数 299
精华内容 119
关键字:

关系数据语言可分为三类