`

学习笔记之继承与派生

阅读更多

面向对象程序设计有4个主要特点:抽象,封装,继承和多态。

1.1继承与派生的概念

在C++中可重用性是通过继承(inheritance)这一机制来实现的。继承是C++的一个只要组成部分.

一个新类从已有的父类那里获得其已有特征,这种现象称为类的继承。通过继承,一个新建子类从已有的父类那里获得父类的特性。

从另一个角度说,从已有的类(父类)产生一个新的子类,称为类的派生。

类的继承是用已有的类来简历专用类的编程技术.

派生类继承了基类的所有数据成员和成员函数,并可以对成员做必要的增加和调整。

一个基类可以派生出多个派生类,每一个派生类又可以作为基类再派生出新的派生类,因此基类和派生类是相对而言的.

一个派生类不仅可以从一个基类派生,也可以从多个基类派生。一个派生类有两个或者多个基类的称为多重继承(multiple inheritance)。

关于派生类和基类的关系,可以表述为:派生类是基类的具体化,而基类则是派生类的抽象。

继承方式包括:public(公用的),private(私有的),protected(保护的).此项是可选的,如果不写此项,则默认是private的。

派生类从基类接收成员,派生类把基类全部的成员(不包括构造函数和析构函数)接手过来,也就是说是没有选择的,不能选择接收其中一部分成员,而舍弃另一部分成员.

不同继承方式决定了基类成员在派生类中的访问属性.

1)公用继承(public inheritance)

基类的公用成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类私有。

2)私有继承(private inheritance)

基类的公有成员和保护成员在派生类中成了私有成员,其私有成员仍为基类私有。

3)受保护的继承(protected inheritance)

基类的公用成员和保护成员在派生类中成了保护成员,其私有成员仍为基类私有.

公用继承:

采用公用继承方式时,基类的公用成员和保护成员在派生类中仍然保持其公用成员和保护成员的属性,而基类的私有成员在派生类中并没有成为派生类的私有成员,它仍然是基类的私有成员,只有基类的成员函数可以应用它,而不能被派生类的成员函数应用,因此就成为派生类中的不可访问的成员。

私用继承

私有基类的公用成员和保护成员在派生类中的访问属性相当于派生类中的私有成员,即派生类的成员函数能访问他们,而在派生类不能访问他们。私有基类的私有成员在派生类中成为不可访问的成员,只有基类的成员函数可以应用他们.一个基类成员在基类中的访问属性和在派生类中的访问属性可能是不同的.

保护继承

如果基类声明了私有成员,那么任何派生类都是不能访问他们的,若希望在派生类中能访问他们,应当把它们声明为保护成员,如果一个类中声明了保护成员,就意味着该类可能要用作基类,在他的派生类中会访问这些成员

比较一下私有继承和保护继承,可以发现,在直接派生类中,以上两种继承方式的作用实际上是相同的;在类外不能访问任何成员,而在派生类中可以通过成员函数访问基类中的公用成员和保护成员。

在派生类中,成员有4种不同的访问属性:

1)公用的,派生类和派生类外都可以访问。

2)受保护的,派生类内可以访问,派生类外不能访问,其下一层的派生类可以访问。

3)私有的,派生类内可以访问,派生类外不能访问

4)不可访问的,派生类内和派生类外都不能访问。

派生类的构造函数和析构函数

用户在声明类时可以不定义构造函数,系统会自动设置一个默认的构造函数,在定义类对象时会自动调用这个默认的构造函数。这个构造函数实际上是一个空函数,不执行任何操作,如果需要对类中的数据成员初始化,应自己定义构造函数。

构造函数的主要作用是对数据成员初始化,在设计派生类的构造函数时,不仅要考虑派生类所新增的数据成员的初始化,还应当考虑基类的数据成员初始化,也就是说,希望在执行派生类的构造函数时,使派生类的数据库成员和基类的数据成员同时都被初始化,解决这个问题的思路是:在执行派生类的构造函数时,调用基类的构造函数。

其一般形式为

派生类构造函数名(总参数列表):基类构造函数名(参数类别)

{派生类中新增数据成员初始化语句}

请注意:在类中对派生类构造函数作声明时,不包括基类构造函数名及其参数列表,只在定义函数时才将他们列出。

实际上,在派生类构造函数中对基类成员初始化,就是构造函数初始化表,也就是说,不仅可以利用初始化表对构造函数的数据成员初始化,而且可以利用初始化表调用派生类的基类构造函数,实现对基类数据成员的初始化。也可以在同一个构造函数的定义中同时实现这两种功能。

在建立一个对象时,执行构造函数的顺序是:派生类构造函数先调用基类的构造函数,再执行派生类构造函数本身(即派生类构造函数的函数体)

在派生类对象释放时,先执行派生类的析构函数,再执行基类的析构函数。

派生类构造函数的任务应该包括3个部分

对基类数据成员的初始化,对子对象数据成员初始化,对派生类数据成员初始化

程序中派生类构造函数首部如下:

Student1(int n,string nam,int n1,string nam1,int a,string ad):Student(n,nam),monotor(n1,nam1)

归纳起来,定义派生类构造函数的一般形式为

派生类构造函数名(总参数表列):基类构造函数名(参数表列),子对象名(参数表列)

{派生类中新增数据成员初始化语句}

执行派生类构造函数的顺序是:

调用基类构造函数,对基类数据成员初始化

调用子对象构造函数,对子对象数据成员初始化

再执行派生类构造函数本身,对派生类数据成员初始化。

派生类构造函数的总参数表列中的参数,应当包括基类构造函数和子对象的参数列表中的参数,基类构造函数和子对象的次序可以是任意的。如上面的派生类构造函数首部可以写成

Student(int n,string nam,int n1,string nam1,int a,stirng ad):monitor(n1,nam1),Student(n,nam)

如果在基类中没有定义构造函数,或者定义了没有参数的构造函数,那么在定义派生类构造函数时可以不写基类构造函数,因为此时派生类构造函数没有向基类构造函数传递参数的任务。调用派生类构造函数时系统自动首先调用基类的默认构造函数。如果基类和子对象类型的声明中都没有定义带参数的构造函数,而且也不需对派生类自己的数据成员初始化,则可以不必显示的定义派生类构造函数。因为此时派生类构造函数既没有像基类构造函数和子对象构造函数传递参数的任务,也没有对派生类数据成员初始化的任务。在建立派生类对象时,系统会自动调用系统提供的派生类的默认构造函数,并在执行派生类默认构造函数的过程中,调用基类的默认构造函数和子对象类型默认构造函数。

如果在基类或者子对象类型的声明中定义了才参数的构造函数,那么就必选显示的定义派生类构造函数,并在派生类构造函数中写出基类或子对象类型的构造函数及其参数。

如果在基类中既定义无参的构造函数,又定义了有参的构造函数(构造函数重载),则在定义派生类构造函数时,既可以包含基类构造函数及其参数,也可以不包含基类构造函数,在调用派生类构造函数时,根据构造函数的内容决定调用基类的有参构造函数还是无参构造函数。

派生类的析构函数

在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理。调用的顺序和构造函数正好相反;先执行派生类自己的析构函数,对派生类新增加的成员进行清理,然后调用子对象的析构函数,对子对象进行清理,最后调用基类的析构函数,对基类进行清理.

声明多重继承的方法

如果已声明了类A,类B和类C,可以声明多重继承的派生类D:

class D:public A,private B,protected C

{类D新增加的成员}

多重继承派生类的构造函数形式于单继承时的构造函数形式基本相同,只有在初始表中包含多个基类构造函数

派生类构造函数名(总参数列表):基类1构造函数(参数类别),基类2构造函数(参数列表),基类3构造函数(参数列表)

{派生类中新增成员初始化}

各基类的排列顺序任意。派生类构造函数同样为:先调用基类的构造函数,再执行派生类构造函数的函数体.调用基类构造函数的顺序是按照声明派生类时基类出现的顺序.

多重继承一起的二义性问题

虚基类

虚基类的作用: 如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员,在引用这些同名成员时,必选在派生类对象名后增加直接基类名,以避免产生二义性,使其唯一地标识一个成员.

在一个类中 保留间接共同基类的多份同名成员,这种现象是人们不希望出现的。

C++提供虚基类(virtual base class)的方法,使得在继承间接共同基类时只保留一份成员.

现在,将类A声明为虚基类,方法如下

class A//声明基类A

{......};

class B:virtual public A//声明类B是类A的公用派生类,A是B的虚基类

{......};

class C:virtual public A//声明类C是类A的公用派生类,A是C的虚基类

{......};

注意:虚基类并不是声明基类时声明的,而是在声明派生类时,指定继承方式时声明的。因为一个基类可以在生成一个派生类时作为虚基类,而在声明另一个派生类时不作为虚基类,声明虚基类的一般形式为

class 派生类名:virtual 继承方式 基类名

经过这样的声明后,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次。

需要注意:为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类,否则仍然会出现对基类的多次继承.

虚基类的初始化

C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类(如类B和类C)对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化.

注意:在定义派生类的构造函数时,与以往使用的方法有所不同。规定:在最后的派生类中不仅仅要负责对其直接基类进行初始化,还要负责对虚基类初始化

使用多重继承时要十分小心,经常会出现二义性问题。许多专业人员认为:不要提倡在程序中使用多重继承,只有在比较简单和不易出现二义性的情况下或实在必要时才使用多重继承,能用单一继承解决的问题不要使用多重继承,也是由于这个原因,有些面向多谢的程序设计语言(如java,smalltalk)并不支持多重继承.

基类与派生类的转换

基类于派生类对象之间有赋值兼容关系,由于派生类中包含从基类继承的成员,因此可以将派生类的值赋给基类对象,在用到基类对象的时候可以其子类对象代替.

1)派生类对象可以向基类对象赋值。

A a1;//定义基类A对象a1

B b1;//定义类A的公用派生类B的对象b1

a1 = b1;//用派生类B对象b1对基类对象a1赋值

在赋值时舍弃派生类自己的成员,实际上,所谓赋值只是对数据成员赋值,对成员函数不存在赋值问题。

请注意:赋值后不能企图通过对象a1去访问派生类对象b1的成员,因为b1的成员于a1的成员是不同的。

应当注意,只能用子类对象对其基类对象赋值,而不能用基类对象对其之类对象赋值,理由是显然的,因为基类对象不包括派生类的成员,无法对派生类的成员赋值

2)派生类对象可以提到基类对象向基类对象的引用进行赋值或者初始化.

如已定义了基类A对象a1,可以定义a1的引用变量

A a1;//定义基类A对象a1

B b1;//定义公用派生类B对象b1

A &r = a1;//定义基类A对象的引用变量r,并哦那个a1对其初始化

这时,引用变量r是a1的别名,r和a1共享同一段储存单元。也可以用子类对象初始化引用变量r,将上面最后一行改为:

A &r = b1;//定义基类A对象的引用变量r,并用派生类B对象b1对其初始化

或者保留上面第3行"A &r = a1;",而对r重新赋值:

r = b1;//用派生类B对象b1对a1的引用变量r赋值

3)如果函数的参数是基类对象或者基类对象的引用,相应的实参可以用子类对象

4)派生类对象的地址可以赋给指向基类对象的指针变量,也就是说,指向基类对象的指针变量也可以指向派生类对象.

用指向基类对象的指针变量指向子类对象是合法的,安全的,不会出现遍以上的错误,但在应用上却不能完全满足人们的希望,人们有时希望通过使用基类指针能够基类和子类对象的成员。在下一章就要解决这个问题。办法是使用虚函数和多态性

分享到:
评论

相关推荐

    python基础学习笔记整理

    2.2.2、类的继承与派生 2.2.3、封装、反射、单例、元类 2.2.4、模块编程(工具类) 2.3、网络编程 2.4、并发编程 2.5、操作数据库 2.5.1、mysql 2.5.2、redis 2.5.3、MongoDB 2.6、API接口 3、Java调用Python

    net学习笔记及其他代码应用

    接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它...

    ASPX.NET学习笔记

    第4章 ASP.NET的网页代码模型及生命周期 ...在ASP.NET中,能够使用面向对象的思想和软件开发中的一些思想,例如封装、派生、继承以及高级的设计模式等。本章首先介绍ASP.NET中最重要的概念---网页代码模型。

    C#中面向对象编程机制之继承学习笔记

    继承反应了类和类之间的关系。 世界上很多事物都是有共性的,共性的那一部分我们就抽象为基类,用于派生其它类,这样提高了代码的复用性,使得代码的结构清晰易读,而且易于代码的扩展和维护。 C#的继承只能继承自一...

    java学习笔记 - 9

    4)派生类继承超类后,派生类具有:派生类+超类的属性和行为 5)一个超类可以有多个派生类 6)继承具有传递性 7)java规定:构造派生类之前必须先构造超类 3.super:指代当前对象的超类对象

    java学习笔记

    继承是面向对象的三大特征之一,是java中实现代码重用的重要手段之一   父类大大  部门类:父类(基类)  人事部类,研发部类,市场部类:子类(派生类) 继承:使得一个类A能够直接使用另外一个类B的属性...

    c#学习笔记.txt

    c#学习笔记(1) 51099在线学习网发布 文章来源:网络收集 发布时间:2006-05-25 字体: [大 中 小] 51099在线学习网 http://www.51099.com 1, 结构(struct) 与 类(class) [attributes] [modifiers] struct ...

    C++类封装设计继承

    结合本人博客C++学习笔记——类的继承写的测试代码。以CWorker为抽象基类,派生出CEmployee员工类和CStudent学生类,从CEmployee员工类又派生出CEngineer工程师类。

    c#学习笔记——学习心得

    密封类:密封类不能作为基类被继承,但可以继承别的类或接口,密封类中不能声明受保护的成员或虚成员,密封类不能声明为抽象的,声明密封类用sealed.密封方法只能用于对基类的虚方法进行实现,声明密封方法时同时...

    php学习笔记之面向对象

    public 公有的:本类,子类,外部对象都可以调用 protected 受保护的:本类 子类,可以执行,外部对象不可以调用 ...继承性就是派生类(子类)自动继承一个或多个基类(父类)中的属性与方法,并可以重写

    python学习笔记(十二)面向对象2

    面向对象 ...总结:其实他们是一回事,只是描述问题的侧重点不同(继承侧重相同点,派生侧重不同点) 可以简单理解 儿子 相对于父亲 可以对父亲的家产创新 也可以变卖 可以取其精华取其糟粕 可以青出于蓝而胜

    C++讲稿(清华大学 郑 莉ppt)

    C++语言程序设计(第三版),入门以提升。 第六章 数组 指针与字符串 第七章 继承与派生 第八章 多态性 第九章 群体类 和群体数据的组织 .......

    Java核心技术卷学习笔记 第一天

    首先今天我学习的是Java中非常重要的三大特性之一继承,首先继承的关键字是extends 它表明正在构造的新类派生于一个已存在的类。这个已存在的类被称为超类(superclass),基类(baseclass)或父类(prantclass);...

    C#中面向对象编程机制之多态学习笔记

    例如:基类动物都有吃的方法,但是不同的动物吃的东西就会不一样,例如狼吃肉,羊吃草,这样“吃”的这个方法就要在派生类里面重新实现以下,运行时,通过指向基类的指针,来调用实现派生类中的方法。 接下来举例...

    C++基础知识总结

    包括类、继承与派生、多态性与虚函数等。如果你原就有一定的C语言基础,这份文档会让你学起C++来事半功倍,当然没有C语言基础的也可学习,因为文档里不仅仅写了二者的区别,也对C++里的函数等做了一个详细的阐述!

    ARCGIS开发笔记

    对象之间可以派生继承 ,接口自然也可以了,因为接口只是一种软件定义的接口,它是符合COM标准的,所以也可以说这是硬性规定的东西,目的是希望全世界都来遵守这个科学的规范,只有这样才能使写出的程序更好看,更有...

Global site tag (gtag.js) - Google Analytics