《(7)--第7章面向对象程序设计.pdf》由会员分享,可在线阅读,更多相关《(7)--第7章面向对象程序设计.pdf(35页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、第七章 组合、继承与多态性计算机科学与工程学院学习目标学习目标理解基类和派生类的概念理解基类和派生类的概念能够通过继承建立新类,掌握多继承和多派生方法能够通过继承建立新类,掌握多继承和多派生方法理解并掌握如何提高软件的重用性理解并掌握如何提高软件的重用性理解继承与组合的特点理解继承与组合的特点理解多态性,掌握虚函数的设计方法理解多态性,掌握虚函数的设计方法计算机科学与工程学院组合组合&继承继承对于比较简单的类,其数据成员可能都是基本数据类型,对于比较简单的类,其数据成员可能都是基本数据类型,但对于某些复杂的类来说,其某些数据成员可能又是另一但对于某些复杂的类来说,其某些数据成员可能又是另一些类
2、类型,这就形成了类的组合些类类型,这就形成了类的组合(聚集聚集)。计算机科学与工程学院组合组合&继承继承例如:以类方式保存某个班级的名称、人数及每个学例如:以类方式保存某个班级的名称、人数及每个学生的学号、姓名、学习成绩生的学号、姓名、学习成绩计算机科学与工程学院组合组合&继承继承继承性是自然界普遍存在的一种现象,是从先辈那里得到继承性是自然界普遍存在的一种现象,是从先辈那里得到已有的特征和行为。类的继承就是新类从已有类那里获得已有的特征和行为。类的继承就是新类从已有类那里获得已有的属性和行为,或者说是基类派生了具有基类特征又已有的属性和行为,或者说是基类派生了具有基类特征又有新特征的派生有新
3、特征的派生类。类。计算机科学与工程学院继承继承继承是软件可重用性的一种形式,新类通过继承从现有类继承是软件可重用性的一种形式,新类通过继承从现有类中吸取其属性和行为,并对其进行覆盖或改写,产生新类中吸取其属性和行为,并对其进行覆盖或改写,产生新类所需要的功能。同样的,新类也可以派生出其他更所需要的功能。同样的,新类也可以派生出其他更“新新”的类的类。类继承语法如下类继承语法如下class 子类名子类名:public|private|protected父类名父类名计算机科学与工程学院继承继承C+提供了三种继承的方式:公有的提供了三种继承的方式:公有的(public)、私有的、私有的(privat
4、e)、受保护的、受保护的(protected)。公有继承公有继承 基类的基类的private、public和和protected成员的访问属性在派生类成员的访问属性在派生类中保持不变。中保持不变。派生类中继承的成员函数可以直接访问基类中所有成员,派生派生类中继承的成员函数可以直接访问基类中所有成员,派生类中新增的成员函数只能访问基类的类中新增的成员函数只能访问基类的public和和protected成员,成员,不能访问基类的不能访问基类的private成员。成员。通过派生类的对象只能访问基类的通过派生类的对象只能访问基类的public成员。成员。计算机科学与工程学院继承继承 例如,矩形移动例如
5、,矩形移动计算机科学与工程学院西安理工大学继承继承受保护继承受保护继承 基类的基类的public和和protected成员都以成员都以protected身份出现在派生类身份出现在派生类中。中。派生类中新增的成员函数可以直接访问基类中的派生类中新增的成员函数可以直接访问基类中的public和和protected成员,但不能访问基类的成员,但不能访问基类的private成员。成员。通过派生类的对象不能访问基类中的任何成员。通过派生类的对象不能访问基类中的任何成员。例如:修改例如:修改7_2计算机科学与工程学院西安理工大学继承继承私有继承私有继承 基类的基类的public和和protected成员都
6、以成员都以private身份出现在派生类中。身份出现在派生类中。派生类中新增的成员函数可以直接访问基类中的派生类中新增的成员函数可以直接访问基类中的public和和protected成员,但不能访问基类的成员,但不能访问基类的private成员。成员。通过派生类的对象不能访问基类中的任何成员。通过派生类的对象不能访问基类中的任何成员。例如:修改例如:修改7_2计算机科学与工程学院西安理工大学构造与析构次序构造与析构次序派生类构造函数的格式为:派生类构造函数的格式为:当采用继承方式创建子类对象时,首先从父类开始执行构造函当采用继承方式创建子类对象时,首先从父类开始执行构造函数,父类的成员,然后再
7、执行子类的构造函数,子类成员;当数,父类的成员,然后再执行子类的构造函数,子类成员;当撤销子类对象时,执行相反的顺序,即首先撤销子类的成员,撤销子类对象时,执行相反的顺序,即首先撤销子类的成员,执行子类的析构函数,再撤销父类成员,执行父类的析构函数。执行子类的析构函数,再撤销父类成员,执行父类的析构函数。例例7-4 在采用继承方式生成的类中,构造函数与析构函数的调用顺在采用继承方式生成的类中,构造函数与析构函数的调用顺序序class 派生类名派生类名:public|private|protected 基类名基类名public:派生类名派生类名(参数列表参数列表1):基类名基类名(参数列表参数列
8、表2);计算机科学与工程学院西安理工大学继承与组合继承与组合实际工作中往往需要在定义一个新类时这个新类的一部分实际工作中往往需要在定义一个新类时这个新类的一部分内容是从已有类中继承的,还有一部分内容则是需要由其内容是从已有类中继承的,还有一部分内容则是需要由其他类组合的,这就需要把组合和继承放在一起使用。他类组合的,这就需要把组合和继承放在一起使用。例例7-3 建立班级类建立班级类(包括班级名称、人数,学生姓名、包括班级名称、人数,学生姓名、学号、性别、年龄、各门课程成绩,辅导员姓名、性别、学号、性别、年龄、各门课程成绩,辅导员姓名、性别、年龄年龄、工号、工号)。计算机科学与工程学院西安理工大
9、学构造与析构次序构造与析构次序对于组合,给出对象的名字而不是类名。如果在初始化表对于组合,给出对象的名字而不是类名。如果在初始化表达式表中有多于一个构造函数调用,应当用逗号隔开,派达式表中有多于一个构造函数调用,应当用逗号隔开,派生类构造函数的格式为:生类构造函数的格式为:class 派生类名派生类名:public|private|protected 基类名基类名public:派生类名派生类名(参数列表参数列表1):基类名基类名(参数列表参数列表2),组合对象列表组合对象列表;计算机科学与工程学院西安理工大学构造与析构次序构造与析构次序相对于基类和派生类,构造顺序:相对于基类和派生类,构造顺序
10、:调用基类的构造函数;调用基类的构造函数;根据类中声明的顺序构造组合对象;根据类中声明的顺序构造组合对象;派生类中构造函数的执行。派生类中构造函数的执行。例例7-5 组合类中构造与析构函数的调用顺序组合类中构造与析构函数的调用顺序计算机科学与工程学院西安理工大学构造与析构次序构造与析构次序注意:注意:派生类不能继承基类的构造函数和析构函数。当基类有带参数的派生类不能继承基类的构造函数和析构函数。当基类有带参数的构造函数时,派生类必须定义构造函数,以便把参数传递给基类构造函数时,派生类必须定义构造函数,以便把参数传递给基类构造函数。构造函数。当派生类也作为基类使用时,则各派生类只负责其直接的基类
11、的当派生类也作为基类使用时,则各派生类只负责其直接的基类的构造。构造。因为析构函数不带参数,派生类中析构函数的存在不依赖于基类,因为析构函数不带参数,派生类中析构函数的存在不依赖于基类,基类中析构函数的存在也不依赖于派生类。基类中析构函数的存在也不依赖于派生类。例例7-6 继承类中构造与析构函数的调用顺序。继承类中构造与析构函数的调用顺序。计算机科学与工程学院西安理工大学派生类重载基类函数的访问派生类重载基类函数的访问如果在基类中有一个函数名被重载几次,在派生类中又重如果在基类中有一个函数名被重载几次,在派生类中又重定义了这个函数名,则在派生类中会掩盖这个函数的所有定义了这个函数名,则在派生类
12、中会掩盖这个函数的所有基类定义。基类定义。例例7-7 派生类中重载基类函数。派生类中重载基类函数。如果要访问基类中声明的函数,则有以下方法:如果要访问基类中声明的函数,则有以下方法:使用作用域标识符限定。使用作用域标识符限定。data.Number:print(5);/正确,被调用的函数是基类正确,被调用的函数是基类Number的的print(int)避免名称覆盖。避免名称覆盖。计算机科学与工程学院西安理工大学虚函数虚函数多态性是面向对象程序设计的重要特征,重载和虚函数是体现多态性多态性是面向对象程序设计的重要特征,重载和虚函数是体现多态性的两个重要手段。虚函数体现了多态的灵活性,进一步减少冗
13、余信息,的两个重要手段。虚函数体现了多态的灵活性,进一步减少冗余信息,显著提高了软件的可扩充性。显著提高了软件的可扩充性。例例7-8 通过派生类对象间接调用重载函数通过派生类对象间接调用重载函数静态绑定与动态绑定静态绑定与动态绑定 绑定绑定(binding),又称联编,是使一个计算机程序的不同部分彼此关联的,又称联编,是使一个计算机程序的不同部分彼此关联的过程。根据进行绑定所处阶段的不同,有两种不同的绑定方法过程。根据进行绑定所处阶段的不同,有两种不同的绑定方法静态静态绑定和动态绑定。绑定和动态绑定。静态绑定在编译阶段完成,所有绑定过程都在程序开始之前完成。静态静态绑定在编译阶段完成,所有绑定
14、过程都在程序开始之前完成。静态绑定具有执行速度快的特点,因为程序运行之前,编译程序能够进行代绑定具有执行速度快的特点,因为程序运行之前,编译程序能够进行代码优化。码优化。如果编译器在编译阶段不确切地知道把发送到对象的消息和实现消息的如果编译器在编译阶段不确切地知道把发送到对象的消息和实现消息的哪段代码具体联系到一起,而是运行时才把函数调用与函数具体联系在哪段代码具体联系到一起,而是运行时才把函数调用与函数具体联系在一起,就称做动态绑定。一起,就称做动态绑定。计算机科学与工程学院西安理工大学虚函数虚函数虚函数定义格式虚函数定义格式例例7-9 用虚函数方法实现对派生类中重载函数的调用用虚函数方法实
15、现对派生类中重载函数的调用使用虚函数时需要注意以下几点:使用虚函数时需要注意以下几点:必须在基类中声明虚函数,即需要在派生类中重载的函数,必须在基类必须在基类中声明虚函数,即需要在派生类中重载的函数,必须在基类中声明。中声明。虚函数一经声明,在派生类中重载的基类中的函数即是虚函数,不再需虚函数一经声明,在派生类中重载的基类中的函数即是虚函数,不再需要加要加virtual。class 基类名基类名virtual 返回值类型返回值类型 将要在派生类中重载的函数名将要在派生类中重载的函数名(参数列表参数列表);计算机科学与工程学院西安理工大学虚函数虚函数&虚析构函数虚析构函数 只有非静态成员函数可以
16、声明为虚函数。静态成员函数和全局函只有非静态成员函数可以声明为虚函数。静态成员函数和全局函数不能声明为虚函数。数不能声明为虚函数。编译器把名称相同、参数不同的函数看做不同的函数。基类和派编译器把名称相同、参数不同的函数看做不同的函数。基类和派生类中有相同名字但参数列表不同的函数,不需要声明为虚函数。生类中有相同名字但参数列表不同的函数,不需要声明为虚函数。普通对象调用虚函数,系统仍然以静态绑定方式调用函数。因为普通对象调用虚函数,系统仍然以静态绑定方式调用函数。因为编译器编译时能确切知道对象的类型,能确切调用其成员函数。编译器编译时能确切知道对象的类型,能确切调用其成员函数。虚析构函数虚析构函
17、数 构造函数不能声明为虚函数。构造函数不能声明为虚函数。析构函数能够且常常必须是虚函数。析构函数能够且常常必须是虚函数。计算机科学与工程学院西安理工大学虚析构函数虚析构函数虚析构函数声明格式如下虚析构函数声明格式如下纯虚函数和抽象基类纯虚函数和抽象基类 在实际工作中往往需要定义这样一个类,对这个类中的处理函数在实际工作中往往需要定义这样一个类,对这个类中的处理函数(方法方法)只需要说明函数的名称、参数列表,以及返回值的类型,只需要说明函数的名称、参数列表,以及返回值的类型,也就是只提供一个接口,以说明和规范其他程序对此服务的调用。也就是只提供一个接口,以说明和规范其他程序对此服务的调用。vir
18、tual 析构函数名称析构函数名称();计算机科学与工程学院西安理工大学纯虚函数和抽象基类纯虚函数和抽象基类纯虚函数定义格式如下纯虚函数定义格式如下当一个类中存在纯虚函数时,这个类就是抽象类。例如:当一个类中存在纯虚函数时,这个类就是抽象类。例如:virtual 返回值类型返回值类型 函数名称函数名称(参数列表参数列表)=0;class Instrupublic:virtual void play()const=0;/纯虚函数;计算机科学与工程学院西安理工大学补充补充例1、class base public:virtual void func()coutbaseendl;class deriv
19、ed1:public base public:void func()coutderived1endl;class derived2:public base public:void func(int x=12)coutx derived2func();ptr1=&obj2;ptr1-func();return 0;计算机科学与工程学院西安理工大学补充补充例2、class Apublic:int i;void print()coutiinside Aendl;class B:public Apublic:void print()coutiinside Bendl;计算机科学与工程学院西安理工大学补
20、充补充class C:private Apublic:int i;C()A:i=10;void print()coutiinside Cendl;coutA:iinside A:iprint();pb=&b;pb-print();pc=&c;pc-print();return 0;计算机科学与工程学院西安理工大学补充补充例3、class Bpublic:void virtual f()coutb.fendl;virtual B()coutdestroy Bendl;计算机科学与工程学院西安理工大学补充补充class D:public Bpublic:virtual D()coutdestroy
21、 Dendl;class E:public Dpublic:void virtual f()coute.fendl;virtual E()coutdestroy Ef();delete b;B*d=new D;d-f();delete d;B*e=new E;e-f();delete e;return 0;计算机科学与工程学院西安理工大学抽象类抽象类使用抽象类时需注意以下几点使用抽象类时需注意以下几点 抽象类只能用做其他类的基类,不能建立抽象类对象。抽象类处于继承抽象类只能用做其他类的基类,不能建立抽象类对象。抽象类处于继承层次结构的较上层,抽象类自身无法实例化,只能通过继承机制,生成层次结构
22、的较上层,抽象类自身无法实例化,只能通过继承机制,生成抽象类的非抽象派生类,然后再实例化。抽象类的非抽象派生类,然后再实例化。抽象类不能用做参数类型、函数返回值或显式转换的类型。抽象类不能用做参数类型、函数返回值或显式转换的类型。可以声明一个抽象类的指针和引用。通过指针或引用,可以指向并访问可以声明一个抽象类的指针和引用。通过指针或引用,可以指向并访问派生类对象,以访问派生类的成员。派生类对象,以访问派生类的成员。抽象类派生出新的类之后,如果派生类给出所有纯虚函数的函数实现,抽象类派生出新的类之后,如果派生类给出所有纯虚函数的函数实现,这个派生类就可以声明自己的对象,因而不再是抽象类;反之,如
23、果派这个派生类就可以声明自己的对象,因而不再是抽象类;反之,如果派生类没有给出全部纯虚函数的实现,这时的派生类仍然是一个抽象类。生类没有给出全部纯虚函数的实现,这时的派生类仍然是一个抽象类。例例7-11 用抽象类的方法实现例用抽象类的方法实现例7-9例例7-12 虚函数与纯虚函数的使用虚函数与纯虚函数的使用计算机科学与工程学院西安理工大学多重继承多重继承在派生类的声明中,基类名可以有一个,也可以有多个。在派生类的声明中,基类名可以有一个,也可以有多个。如果只有一个基类名,则这种继承方式称为单继承;如果如果只有一个基类名,则这种继承方式称为单继承;如果基类名有多个,则这种继承方式称为多继承,这时
24、的派生基类名有多个,则这种继承方式称为多继承,这时的派生类同时得到了多个已有类的特征。类同时得到了多个已有类的特征。在多继承中,各个基类名之间用逗号隔开。多继承的声明在多继承中,各个基类名之间用逗号隔开。多继承的声明语法如下:语法如下:class 派生类名派生类名:继承方式继承方式基类名基类名1,继承方式继承方式基类名基类名2,继承方式继承方式基类名基类名n计算机科学与工程学院西安理工大学多重继承多重继承例例7-13 多继承的使用多继承的使用多继承中的二义性多继承中的二义性例例7-14 多继承中的二义性举例多继承中的二义性举例如把一个基类定义为虚基类,必须在派生子类时在父类的如把一个基类定义为
25、虚基类,必须在派生子类时在父类的名字前加关键字名字前加关键字virtual。定义格式如下。定义格式如下:例例7-15 虚基类使用方法举例虚基类使用方法举例class 派生类名派生类名:virtual访问权限修饰符访问权限修饰符 父类名父类名;计算机科学与工程学院西安理工大学虚基类的构造函数虚基类的构造函数当使用虚基类时,尤其是带有参数的构造函数的虚基类时,当使用虚基类时,尤其是带有参数的构造函数的虚基类时,最终派生类的构造函数必须对虚基类初始化。不管派生类最终派生类的构造函数必须对虚基类初始化。不管派生类离虚基类多远,都必须对虚基类初始化。离虚基类多远,都必须对虚基类初始化。例例7-16 含虚
26、基类的构造函数使用方法举例含虚基类的构造函数使用方法举例使用虚基类时要注意以下两点使用虚基类时要注意以下两点 必须在派生类的构造函数中调用初始化虚基类的构造函数;必须在派生类的构造函数中调用初始化虚基类的构造函数;给虚基类安排默认构造函数。使得虚基类的使用者变得简单易行。给虚基类安排默认构造函数。使得虚基类的使用者变得简单易行。例例7-17 多继承的构造顺序。多继承的构造顺序。计算机科学与工程学院西安理工大学class baseint i;public:base(int I):i(I)virtual int value()constreturn i;virtual int shift(int
27、x)constreturn x;class der:public basepublic:der(int I):base(I)int value()const return base:value()*2;virtual int shift(int x)constreturn base:value()x;int main()base*B=new base(7),new der(7);coutvalue()=value()endl;coutvalue()=value()endl;coutshift(3)=shift(3)endl;return 0;计算机科学与工程学院西安理工大学class baseint i;public:base(int I=0):i(I)virtual int sum()constreturn i;class der:public baseint j;public:der(int I=0,int J=0):base(I),j(J)int sum()constreturn base:sum()+j;void call(base b)cout“sum=“b.sum()endl;int main()base b(10);der d(10,47);call(b);call(d);return 0;
限制150内