《c++12继承与派生》由会员分享,可在线阅读,更多相关《c++12继承与派生(43页珍藏版)》请在金锄头文库上搜索。
1、第十二章 继承与派生中北大学电子与计算机科学技术学院C+程序设计基础 乔志会1 继承与派生的概念继承与派生的概念继承:是在一个已存在的类的基础上建立一个新继承:是在一个已存在的类的基础上建立一个新的类,的类,已存在的类称为已存在的类称为“基类基类”或或“父类父类”。新新建立的类称为建立的类称为“派生类派生类”或或“子类子类”。一个新类从已有的类那里获得其已有特性,这种一个新类从已有的类那里获得其已有特性,这种现象称为现象称为类的继承类的继承。从已有的类。从已有的类(父类父类)产生一个产生一个新的子类,称为新的子类,称为类的派生类的派生。单继承单继承:一个派生类只从一个基类派生:一个派生类只从一
2、个基类派生多继承多继承:一个派生类有两个或多个基类:一个派生类有两个或多个基类派生类是基类的具体化,而基类则是派生类的抽象派生类是基类的具体化,而基类则是派生类的抽象中北大学电子与计算机科学技术学院C+程序设计基础 乔志会2 派生类的声明方式派生类的声明方式声明派生类的声明派生类的一般形式一般形式为为: class 派生类名派生类名: 继承方式继承方式 基类名基类名 派生类新增加的成员派生类新增加的成员 ;继承方式包括继承方式包括: public(公用的公用的),private(私有的私有的)和和protected(受保护的受保护的),此项是可选的,如果不,此项是可选的,如果不写此项,则默认为
3、写此项,则默认为private(私有的私有的)。例:例:11.611.6中北大学电子与计算机科学技术学院C+程序设计基础 乔志会3 派生类的构成派生类的构成派生类分为两大部分派生类分为两大部分: 一部分是从基类继承来的成一部分是从基类继承来的成员,另一部分是在声明派生类时增加的部分。每员,另一部分是在声明派生类时增加的部分。每一部分均分别包括数据成员和成员函数。一部分均分别包括数据成员和成员函数。构造一个派生类包括以下构造一个派生类包括以下3部分工作部分工作: (1)从基类接收成员。从基类接收成员。 (2) 调整从基类接收的成员调整从基类接收的成员 (3) 在声明派生类时增加的成员在声明派生类
4、时增加的成员中北大学电子与计算机科学技术学院C+程序设计基础 乔志会3 派生类的构成派生类的构成中北大学电子与计算机科学技术学院C+程序设计基础 乔志会4 派生类成员的访问属性派生类成员的访问属性公用继承公用继承(public inheritance)基类的公用成员和保护成员在派生类中保持原有基类的公用成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类私有。访问属性,其私有成员仍为基类私有。私有继承私有继承(private inheritance)基类的公用成员和保护成员在派生类中成了私有基类的公用成员和保护成员在派生类中成了私有成员。其私有成员仍为基类私有。成员。其私有成员仍为基类
5、私有。受保护的继承受保护的继承(protected inheritance)基类的公用成员和保护成员在派生类中成了保护基类的公用成员和保护成员在派生类中成了保护成员,其私有成员仍为基类私有。成员,其私有成员仍为基类私有。中北大学电子与计算机科学技术学院C+程序设计基础 乔志会一、一、公用继承公用继承 1、概念、概念在定义一个派生类时将基类的继承方式指定为在定义一个派生类时将基类的继承方式指定为public的,称为的,称为公用继承公用继承,用公用继承方式建立的,用公用继承方式建立的派生类称为派生类称为公用派生类公用派生类(public derived class),其其基类称为基类称为公用基类公
6、用基类(public base class)。2、公用基类在派生类中的访问属性、公用基类在派生类中的访问属性公用基类的成员公用基类的成员在公用派生类中的访问属性在公用派生类中的访问属性私有成员私有成员不可访问不可访问公用成员公用成员公用公用保护成员保护成员保护保护中北大学电子与计算机科学技术学院C+程序设计基础 乔志会一、一、公用继承公用继承 3、例例11.1二、二、私有继承私有继承 1、概念、概念在声明一个派生类时将基类的继承方式指定为在声明一个派生类时将基类的继承方式指定为private的,称为的,称为私有继承私有继承,用私有继承方式建立,用私有继承方式建立的派生类称为的派生类称为私有派生
7、类私有派生类(private derived class) ,其基类称为其基类称为私有基类私有基类(private base class)。 2、公用基类在派生类中的访问属性、公用基类在派生类中的访问属性中北大学电子与计算机科学技术学院C+程序设计基础 乔志会二、二、私有继承私有继承 2、私有基类在派生类中的访问属性、私有基类在派生类中的访问属性私有基类的成员私有基类的成员在私有派生类中的访问属性在私有派生类中的访问属性私有成员私有成员不可访问不可访问公用成员公用成员私有私有保护成员保护成员私有私有3、例例11.2中北大学电子与计算机科学技术学院C+程序设计基础 乔志会三、三、保护成员和保护继
8、承保护成员和保护继承1、概念、概念由由protected声明的成员称为声明的成员称为“受保护的成员受保护的成员”,或简称或简称“保护成员保护成员”,保护成员可以被派生类的,保护成员可以被派生类的成员函数引用成员函数引用中北大学电子与计算机科学技术学院C+程序设计基础 乔志会三、三、保护成员和保护继承保护成员和保护继承在定义一个派生类时将基类的继承方式指定为在定义一个派生类时将基类的继承方式指定为protected的,称为的,称为保护继承保护继承,用保护继承方式建,用保护继承方式建立的派生类称为立的派生类称为保护派生类保护派生类,其基类称为其基类称为受保护受保护的基类的基类,简称简称保护基类保护
9、基类。2、保护基类在派生类中的访问属性、保护基类在派生类中的访问属性保护基类的成员保护基类的成员在保护派生类中的访问属性在保护派生类中的访问属性私有成员私有成员不可访问不可访问公用成员公用成员保护保护保护成员保护成员保护保护3、例例11.3中北大学电子与计算机科学技术学院C+程序设计基础 乔志会四、四、多级派生时的访问属性多级派生时的访问属性如果有右图所示的派生关系如果有右图所示的派生关系: 类类A为基为基类,类类,类B是类是类A的派生类,类的派生类,类C是类是类B的派生类,则类的派生类,则类C也是类也是类A的派生类。的派生类。类类B称为类称为类A的直接派生类,类的直接派生类,类C称为称为类类
10、A的间接派生类。类的间接派生类。类A是类是类B的直接的直接基类,是类基类,是类C的间接基类。在多级派的间接基类。在多级派生的情况下,各成员的访问属性仍按生的情况下,各成员的访问属性仍按以上原则确定。以上原则确定。中北大学电子与计算机科学技术学院C+程序设计基础 乔志会5 派生类的构造函数和析构函数派生类的构造函数和析构函数构造函数的主要作用是对数据成员初始化,构造函数的主要作用是对数据成员初始化,所以在设计派生类的构造函数时,不仅要考所以在设计派生类的构造函数时,不仅要考虑派生类所增加的数据成员的初始化,还应虑派生类所增加的数据成员的初始化,还应当考虑基类的数据成员的初始化。在执行派当考虑基类
11、的数据成员的初始化。在执行派生类的构造函数时,调用基类的构造函数生类的构造函数时,调用基类的构造函数中北大学电子与计算机科学技术学院C+程序设计基础 乔志会一、一、简单的派生类的构造函数简单的派生类的构造函数1、概念、概念任何派生类都包含基类的成员,简单的派生类只任何派生类都包含基类的成员,简单的派生类只有一个基类,而且只有一级派生有一个基类,而且只有一级派生(只有直接派生类,只有直接派生类,没有间接派生类没有间接派生类),在派生类的数据成员中不包含,在派生类的数据成员中不包含基类的对象基类的对象(即子对象即子对象)。2、其一般形式为:、其一般形式为: 派生类构造函数名(总参数表列)派生类构造
12、函数名(总参数表列): 基类构造基类构造函数名(参数表列)函数名(参数表列) 派生类中新增数据成员初始化语句派生类中新增数据成员初始化语句中北大学电子与计算机科学技术学院C+程序设计基础 乔志会一、一、简单的派生类的构造函数简单的派生类的构造函数3、例例11.54、在建立一个对象时,执行构造函数的顺序是在建立一个对象时,执行构造函数的顺序是: 派生类构造函数先调用基类构造函数;派生类构造函数先调用基类构造函数; 再执行派生类构造函数本身再执行派生类构造函数本身(即派生类构造函数即派生类构造函数的函数体的函数体)。在派生类对象释放时,先执行派生类析构函数在派生类对象释放时,先执行派生类析构函数中
13、北大学电子与计算机科学技术学院C+程序设计基础 乔志会二、二、有子对象的派生类的构造函数有子对象的派生类的构造函数1、概念、概念子对象:类的数据成员中还可以包含类对象子对象:类的数据成员中还可以包含类对象2、派生类构造函数形式:、派生类构造函数形式:派生类构造函数名(总参数表列)派生类构造函数名(总参数表列): 基类构造函数基类构造函数名(参数表列),子对象名名(参数表列),子对象名(参数表列参数表列) 派生类中新增数成员据成员初始化语句派生类中新增数成员据成员初始化语句3、例例11.6中北大学电子与计算机科学技术学院C+程序设计基础 乔志会二、二、有子对象的派生类的构造函数有子对象的派生类的
14、构造函数1、概念、概念子对象:类的数据成员中还可以包含类对象子对象:类的数据成员中还可以包含类对象2、派生类构造函数形式:、派生类构造函数形式:派生类构造函数名(总参数表列)派生类构造函数名(总参数表列): 基类构造函数基类构造函数名(参数表列),子对象名名(参数表列),子对象名(参数表列参数表列) 派生类中新增数成员据成员初始化语句派生类中新增数成员据成员初始化语句3、例例11.6中北大学电子与计算机科学技术学院C+程序设计基础 乔志会二、二、有子对象的派生类的构造函数有子对象的派生类的构造函数4、执行派生类构造函数的顺序是:执行派生类构造函数的顺序是: 调用基类构造函数,对基类数据成员初始
15、化调用基类构造函数,对基类数据成员初始化 调用子对象构造函数,对子对象数据成员初调用子对象构造函数,对子对象数据成员初始化始化 再执行派生类构造函数本身,对派生类数据再执行派生类构造函数本身,对派生类数据成员初始化成员初始化中北大学电子与计算机科学技术学院C+程序设计基础 乔志会三、三、多层派生时的构造函数多层派生时的构造函数例例11.711.7中北大学电子与计算机科学技术学院C+程序设计基础 乔志会四、四、派生类构造函数的特殊形式派生类构造函数的特殊形式(1) 当不需要对派生类新增的成员进行任何初始当不需要对派生类新增的成员进行任何初始化操作时,派生类构造函数的函数体可以为空,化操作时,派生
16、类构造函数的函数体可以为空,即构造函数是空函数即构造函数是空函数(2) 如果在基类中没有定义构造函数,或定义了如果在基类中没有定义构造函数,或定义了没有参数的构造函数,那么在定义派生类构造函没有参数的构造函数,那么在定义派生类构造函数时可不写基类构造函数数时可不写基类构造函数(3)如果在基类和子对象类型的声明中都没有定义如果在基类和子对象类型的声明中都没有定义带参数的构造函数,而且也不需对派生类自己的带参数的构造函数,而且也不需对派生类自己的数据成员初始化,则可以不必显式地定义派生类数据成员初始化,则可以不必显式地定义派生类构造函数。构造函数。中北大学电子与计算机科学技术学院C+程序设计基础
17、乔志会四、四、派生类构造函数的特殊形式派生类构造函数的特殊形式(4)如果在基类或子对象类型的声明中定义了带参如果在基类或子对象类型的声明中定义了带参数的构造函数,那么就必须显式地定义派生类构数的构造函数,那么就必须显式地定义派生类构造函数,并在派生类构造函数中写出基类或子对造函数,并在派生类构造函数中写出基类或子对象类型的构造函数及其参数表象类型的构造函数及其参数表(5)如果在基类中既定义无参的构造函数,又定义如果在基类中既定义无参的构造函数,又定义了有参的构造函数了有参的构造函数(构造函数重载构造函数重载),则在定义派,则在定义派生类构造函数时,既可以包含基类构造函数及其生类构造函数时,既可
18、以包含基类构造函数及其参数,也可以不包含基类构造函数。在调用派生参数,也可以不包含基类构造函数。在调用派生类构造函数时,根据构造函数的内容决定调用基类构造函数时,根据构造函数的内容决定调用基类的有参的构造函数还是无参的构造函数类的有参的构造函数还是无参的构造函数中北大学电子与计算机科学技术学院C+程序设计基础 乔志会五、五、派生类的析构函数派生类的析构函数调用的派生类析构函数的顺序调用的派生类析构函数的顺序:1、执行派生类自己的析构函数,对派生类新增、执行派生类自己的析构函数,对派生类新增加的成员进行清理加的成员进行清理2、调用子对象的析构函数,对子对象进行清理、调用子对象的析构函数,对子对象
19、进行清理3、调用基类的析构函数,对基类进行清理、调用基类的析构函数,对基类进行清理中北大学电子与计算机科学技术学院C+程序设计基础 乔志会6 多重继承多重继承一、一、声明多重继承的方法声明多重继承的方法如果已声明了类如果已声明了类A、类类B和类和类C,可以声明多重继可以声明多重继承的派生类承的派生类D: class D:public A,private B,protected C 类类D新增加的成员新增加的成员中北大学电子与计算机科学技术学院C+程序设计基础 乔志会二、二、多重继承派生类的构造函数多重继承派生类的构造函数派生类构造函数名派生类构造函数名(总参数表列总参数表列): 基类基类1构造
20、函数构造函数(参数表列参数表列), 基类基类2构造函数构造函数(参数表列参数表列), 基类基类3构构造函数造函数 (参数表列参数表列) 派生类中新增数成员据成员初始化语句派生类中新增数成员据成员初始化语句派生类构造函数的执行顺序同样为派生类构造函数的执行顺序同样为: 先调用基类的构造函数,再执行派生类构造函数的先调用基类的构造函数,再执行派生类构造函数的函数体函数体调用基类构造函数的顺序是按照声明派生类时基类调用基类构造函数的顺序是按照声明派生类时基类出现的顺序出现的顺序例:例:11.8中北大学电子与计算机科学技术学院C+程序设计基础 乔志会三、三、多重继承引起的二义性问题多重继承引起的二义性
21、问题最常见的问题就是继承的成员同名而产生的二义最常见的问题就是继承的成员同名而产生的二义性性(ambiguous)问题问题例:类例:类A和类和类B中都有成员函数中都有成员函数display和数据成员和数据成员a,类类C是类是类A和类和类B的直接派生类,分三种情况讨的直接派生类,分三种情况讨论:论: 1、两个基类有同名成员、两个基类有同名成员 2、两个基类和派生类三者都有同名成员、两个基类和派生类三者都有同名成员 3、类、类A和类和类B是从同一个基类派生的是从同一个基类派生的中北大学电子与计算机科学技术学院C+程序设计基础 乔志会三、三、多重继承引起的二义性问题多重继承引起的二义性问题1、两个基
22、类有同名成员、两个基类有同名成员中北大学电子与计算机科学技术学院C+程序设计基础 乔志会三、三、多重继承引起的二义性问题多重继承引起的二义性问题1、两个基类有同名成员、两个基类有同名成员C c1;(1) 对象名访问用基类名限定对象名访问用基类名限定 c1.A:a=3; c1.A:disaplay( );(2) 派生类成员函数内访问基类派生类成员函数内访问基类 A:a=3; A:disaplay( );中北大学电子与计算机科学技术学院C+程序设计基础 乔志会三、三、多重继承引起的二义性问题多重继承引起的二义性问题1、两个基类有同名成员、两个基类有同名成员 b;中北大学电子与计算机科学技术学院C+
23、程序设计基础 乔志会三、三、多重继承引起的二义性问题多重继承引起的二义性问题2、两个基类和派生类三者都有同名成员、两个基类和派生类三者都有同名成员基类的同名成员在派生类中被屏蔽,成为基类的同名成员在派生类中被屏蔽,成为“不可不可见见”的,或者说,派生类新增加的同名成员覆盖的,或者说,派生类新增加的同名成员覆盖了基类中的同名成员。了基类中的同名成员。如果在定义派生类对象的模块中通过对象名访问如果在定义派生类对象的模块中通过对象名访问同名的成员,则访问的是派生类的成员。同名的成员,则访问的是派生类的成员。中北大学电子与计算机科学技术学院C+程序设计基础 乔志会三、三、多重继承引起的二义性问题多重继
24、承引起的二义性问题3、两个基类和派生类三者都有同名成员、两个基类和派生类三者都有同名成员中北大学电子与计算机科学技术学院C+程序设计基础 乔志会三、三、多重继承引起的二义性问题多重继承引起的二义性问题3、两个基类和派生类三者都有同名成员、两个基类和派生类三者都有同名成员class Npublic:int a;void display()coutA:a=”aendl;class A:public Npublic:int a1;class B:public Npublic:int a2; ;class C :public A,public Bpublic :int a3;void show( )c
25、outa3=a3endl;int main( )C c1;c1.A:a=3; c1.A:display();中北大学电子与计算机科学技术学院C+程序设计基础 乔志会三、三、多重继承引起的二义性问题多重继承引起的二义性问题3、两个基类和派生类三者都有同名成员、两个基类和派生类三者都有同名成员中北大学电子与计算机科学技术学院C+程序设计基础 乔志会四、四、虚基类虚基类1、虚基类的作用、虚基类的作用C+提供虚基类提供虚基类(virtual base class)的方法,使得的方法,使得在继承间接共同基类时只保留一份成员,声明的一在继承间接共同基类时只保留一份成员,声明的一般形式为:般形式为: cla
26、ss 派生类名派生类名: virtual 继承方式继承方式 基类名基类名声明后,当基类通过多条派生路径被一个派生类继声明后,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次承时,该派生类只继承该基类一次中北大学电子与计算机科学技术学院C+程序设计基础 乔志会中北大学电子与计算机科学技术学院C+程序设计基础 乔志会D类类int data;int data_b;int data_cvoid fun( );int data_d;void fun_d( );中北大学电子与计算机科学技术学院C+程序设计基础 乔志会2、虚基类的初始化、虚基类的初始化如果在虚基类中定义了带参数的构造函数,
27、而且没如果在虚基类中定义了带参数的构造函数,而且没有定义默认构造函数,则在其所有派生类中,通过有定义默认构造函数,则在其所有派生类中,通过构造函数的初始化表对虚基类进行初始化。构造函数的初始化表对虚基类进行初始化。class A A(int i) ;class C :virtual public A C(int n):A(n) ;class B :virtual public A B(int n):A(n) ;class D :public B,public C D(int n):A(n),B(n),C(n) ;中北大学电子与计算机科学技术学院C+程序设计基础 乔志会2、虚基类的初始化、虚基类
28、的初始化规定规定: 在最后的派生类中不仅要负责对其直接基类在最后的派生类中不仅要负责对其直接基类进行初始化,还要负责对虚基类初始化。进行初始化,还要负责对虚基类初始化。C+编译系统只执行最后的派生类对虚基类的构造编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类函数的调用,而忽略虚基类的其他派生类(如类如类B和和类类C) 对虚基类的构造函数的调用,这就保证了虚基对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。类的数据成员不会被多次初始化。3、虚基类的应用、虚基类的应用例:例:11.9中北大学电子与计算机科学技术学院C+程序设计基础 乔志会7 基类
29、与派生类的转换基类与派生类的转换只有公用派生类才是基类真正的子类型,它完整地只有公用派生类才是基类真正的子类型,它完整地继承了基类的功能。继承了基类的功能。基类与派生类对象之间有赋值兼容关系,由于派生基类与派生类对象之间有赋值兼容关系,由于派生类中包含从基类继承的成员,因此可以将派生类的类中包含从基类继承的成员,因此可以将派生类的值赋给基类对象,在用到基类对象的时候可以用其值赋给基类对象,在用到基类对象的时候可以用其子类对象代替。具体表现在以下几个方面子类对象代替。具体表现在以下几个方面: 1、派生类对象可以向基类对象赋值、派生类对象可以向基类对象赋值 2、派生类对象可以替代基类对象向基类对象
30、、派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化的引用进行赋值或初始化 3、如果函数的参数是基类对象或基类对象的、如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象引用,相应的实参可以用子类对象 4、指向基类对象的指针变量也可以指向派生、指向基类对象的指针变量也可以指向派生类对象类对象中北大学电子与计算机科学技术学院C+程序设计基础 乔志会1、派生类对象可以向基类对象赋值、派生类对象可以向基类对象赋值中北大学电子与计算机科学技术学院C+程序设计基础 乔志会2、派生类对象可以替代基类对象向基类对象的引用进、派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化行赋
31、值或初始化A a1; B b1; A &r=a1;A a1; B b1; A &r=b1;A a1; B b1; A &r=a1;r=b1;中北大学电子与计算机科学技术学院C+程序设计基础 乔志会3、如果函数的参数是基类对象或基类对象的引用,相、如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象应的实参可以用子类对象A a1; B b1;void fun(A & r) coutr.numendl;fun(b1);4、指向基类对象的指针变量也可以指向派生类对象、指向基类对象的指针变量也可以指向派生类对象例:例:11.10中北大学电子与计算机科学技术学院C+程序设计基础 乔志会8 继承与组合继承与组合9 继承在软件开发中的重要意义继承在软件开发中的重要意义在一个类中以另一个类的对象作为数据在一个类中以另一个类的对象作为数据成员的,称为类的组合成员的,称为类的组合(composition)。类的组合和继承一样,是软件重用的重类的组合和继承一样,是软件重用的重要方式。组合和继承都是有效地利用已要方式。组合和继承都是有效地利用已有类的资源。继承是纵向的,组合是横有类的资源。继承是纵向的,组合是横向的。向的。中北大学电子与计算机科学技术学院C+程序设计基础 乔志会本章结束本章结束