《第2章面向对象程序设计基础PPT讲稿.ppt》由会员分享,可在线阅读,更多相关《第2章面向对象程序设计基础PPT讲稿.ppt(87页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第2章面向对象程序设计基础1第1页,共87页,编辑于2022年,星期一教学目标:v了解面向对象的基本概念了解面向对象的基本概念v 熟练掌握类、对象、派生类的定义和使用熟练掌握类、对象、派生类的定义和使用v 掌握类的构造函数和析构函数的定义和特点掌握类的构造函数和析构函数的定义和特点v 能够利用虚函数实现多态性能够利用虚函数实现多态性v熟悉友元的特性熟悉友元的特性 2第2页,共87页,编辑于2022年,星期一教学内容:2.1 面向对象的基本概念面向对象的基本概念2.2 类和对象的定义类和对象的定义2.3 继承性和派生类继承性和派生类 2.4 多态性多态性2.5 友元友元2.6 模板模板3第3页,
2、共87页,编辑于2022年,星期一2.1 面向对象的基本概念v观点:观点:自然界是由实体(对象)所组成。v程序设计方法:程序设计方法:使用面向对象的观点来描述、模仿并处理现实问题。v要求:要求:高度概括、分类、和抽象。v目的:目的:实现软件设计的产业化。程序=多个对象+消息4第4页,共87页,编辑于2022年,星期一类和对象v对象是现实世界中一个实际存在的事物对象是现实世界中一个实际存在的事物对象的静态特征对象的静态特征对象的行为对象的行为v类是具有相同属性和行为的一组对象的集合,它为属类是具有相同属性和行为的一组对象的集合,它为属于该类的全部对象提供了统一的抽象描述,其内部包括于该类的全部对
3、象提供了统一的抽象描述,其内部包括属性和行为两个主要部分。属性和行为两个主要部分。v对象之间的交互:发送消息对象之间的交互:发送消息5第5页,共87页,编辑于2022年,星期一封装v封装的目的是隐藏对象的内部的实现细节。封装的目的是隐藏对象的内部的实现细节。v通过封装,可以将对象的外部接口与内部的实现细通过封装,可以将对象的外部接口与内部的实现细节分开。节分开。v目的是目的是增强安全性增强安全性和和简化编程简化编程,使用者不必了解具,使用者不必了解具体的实现细节,而只需要通过外部接口,以特定的访问体的实现细节,而只需要通过外部接口,以特定的访问权限,来使用类的成员。权限,来使用类的成员。6第6
4、页,共87页,编辑于2022年,星期一继承v继承是继承是C+中支持层次分类的一种机中支持层次分类的一种机制,允许程序员在保持原有类特性的基制,允许程序员在保持原有类特性的基础上,对新类进行更具体的说明。础上,对新类进行更具体的说明。v实现:声明派生类实现:声明派生类2.37第7页,共87页,编辑于2022年,星期一多态性v多态:同一名称,不同的功能实现方式。多态:同一名称,不同的功能实现方式。v目的:目的:标识统一,减少程序中标识符的个数;标识统一,减少程序中标识符的个数;接口统一,增加程序的灵活性接口统一,增加程序的灵活性v实现:实现:2.4编译时的多态性:重载函数编译时的多态性:重载函数运
5、行时的多态性:虚函数运行时的多态性:虚函数8第8页,共87页,编辑于2022年,星期一2.2 类与对象的定义v类是类是C+的灵魂,如果不真正掌握类,就不能真正的灵魂,如果不真正掌握类,就不能真正掌握掌握C+vC+中的类就是一种用户自己定义的数据类型,和其它中的类就是一种用户自己定义的数据类型,和其它数据类型不同的是,组成这种类型的不仅可以有数据,数据类型不同的是,组成这种类型的不仅可以有数据,而且可以有对数据进行操作的函数。而且可以有对数据进行操作的函数。v为了封装的需要,一般将类的定义放在一个为了封装的需要,一般将类的定义放在一个.h文件中,文件中,而将类的成员函数的实现放在一个而将类的成员
6、函数的实现放在一个.cpp文件中。文件中。9第9页,共87页,编辑于2022年,星期一2.2.1 类的定义class 类名称类名称 public:公有成员(外部接口)公有成员(外部接口)private:私有成员私有成员 protected:保护成员保护成员;类与外部的接口只允许本类中的函数访问(可省略)只能由本类及其派生类的成员函数访问是必需的成员访问说明符10第10页,共87页,编辑于2022年,星期一例2.1:圆类的定义见教材24页v/Circle.h文件,类文件,类CCircle的定义的定义vconst double PI=3.14159;/定义圆周率为符号常量定义圆周率为符号常量vcl
7、ass CCircle /定义类定义类vvpublic:vCCircle()/构造函数构造函数vradius=1;v virtual CCircle()/析构函数析构函数vinline double Area()const;/计算圆的面积计算圆的面积vdouble Circumference()const;/计算圆的周长计算圆的周长vdouble GetRadius()const v /取得圆的半径取得圆的半径vreturn radius;vvoid SetRadius(double r)/为半径设置新值为半径设置新值vradius=r0?r:1;vprivate:v double radiu
8、s;/数据成员数据成员v;vinline double CCircle:Area()const v/计算圆的面积计算圆的面积v v return PI*radius*radius;v/Circle.cpp文件,类文件,类CCircle的成员函数的实现的成员函数的实现#include“Circle.h”double CCircle:Circumference()const /计算圆的周长计算圆的周长 return 2*PI*radius;11第11页,共87页,编辑于2022年,星期一v类的数据成员(成员变量)类的数据成员(成员变量):与一般的变量声明相同,:与一般的变量声明相同,但需要将它放在
9、类的声明体中,一般为但需要将它放在类的声明体中,一般为私有访问私有访问属性。属性。v类的成员函数定义:类的成员函数定义:在类中说明原型,类外给出函数体实现,函数名之前必须加类在类中说明原型,类外给出函数体实现,函数名之前必须加类名和作用域区分符限定名和作用域区分符限定将成员函数定义为内联函数:将成员函数定义为内联函数:v成员函数在类的内部定义,此时无须使用inline关键字,成员函数自动为内联函数。v成员函数在类内声明,在类外定义。但在类外定义时,要加关键字inline。类的成员函数允许声明为说明:12第12页,共87页,编辑于2022年,星期一const成员函数vconst成员函数:成员函数
10、:不修改数据成员的值,不修改数据成员的值,在程序中如果不小心修改了这个成员函数中的对象,则在程序中如果不小心修改了这个成员函数中的对象,则编译器会产生一个语法错误信息。编译器会产生一个语法错误信息。vconst const 成员函数的定义方法:成员函数的定义方法:在函数的原型和定义中,在函数参数表和函数定义的在函数的原型和定义中,在函数参数表和函数定义的左花括号之间加入左花括号之间加入constconst关键字。关键字。v类的成员函数允许重载,允许带缺省参数值。类的成员函数允许重载,允许带缺省参数值。13第13页,共87页,编辑于2022年,星期一2.2.2 构造函数性质:性质:v与类同名、无
11、返回类型与类同名、无返回类型v在对象创建时在对象创建时由系统自动调用由系统自动调用v允许允许重载重载、带缺省值带缺省值v如果类中未声明,则系统自动生成一个如果类中未声明,则系统自动生成一个缺省形缺省形式式的构造函数,形如:类名()的构造函数,形如:类名()作用:对象的初始化14第14页,共87页,编辑于2022年,星期一构造函数举例-CCircle类class CCircle public:CCircle()/无参(缺省)构造函数无参(缺省)构造函数 radius=1;CCircle(double r);/带参构造函数带参构造函数 private:double radius;CCircle:C
12、Circle(double r)radius=r0?r:1;两个构造函数合成一个带缺省值的构造函数两个构造函数合成一个带缺省值的构造函数CCircle(double r=1);/构造函数原型构造函数原型CCircle:CCircle(double r)/构造函数定义构造函数定义 radius=r0?r:1;15第15页,共87页,编辑于2022年,星期一拷贝构造函数拷贝构造函数是一种特殊的构造函数,其形参为本类对象的引用。拷贝构造函数是一种特殊的构造函数,其形参为本类对象的引用。class 类名类名public:类名(形参);类名(形参);/构造函数构造函数 类名(类名(类名类名&对象名对象名
13、););/拷贝构造函数拷贝构造函数 ;类名类名:类名(类名类名(类名&对象名)对象名)/拷贝构造函数的实现拷贝构造函数的实现 函数体函数体 16第16页,共87页,编辑于2022年,星期一拷贝构造函数举例class CCircle public:CCircle(float r=1);CCircle(const CCircle&c)/拷贝构造函数 radius=c.radius;17第17页,共87页,编辑于2022年,星期一v当用类的一个对象去初始化该类的另一个对象时系统当用类的一个对象去初始化该类的另一个对象时系统自动调用它实现对象的拷贝赋值。自动调用它实现对象的拷贝赋值。void main
14、()CCircle c1;CCircle c2(c1);/拷贝构造函数被调用拷贝构造函数被调用拷贝构造函数调用之一18第18页,共87页,编辑于2022年,星期一拷贝构造函数调用之二v若函数的形参为类对象按值传递时,实参赋值给形参,若函数的形参为类对象按值传递时,实参赋值给形参,将自动调用复制构造函数,将自动调用复制构造函数,例如:例如:void fun1(CCircle c)coutc.GetRadius()-成员名成员名成员名成员名”方式方式25第25页,共87页,编辑于2022年,星期一this指针v在在C+中为每个非静态成员函数提供了一个名字为中为每个非静态成员函数提供了一个名字为th
15、is的指针,当进行成员函数调用时,系统自动将的指针,当进行成员函数调用时,系统自动将调用此成员函数的对象的地址作为一个隐含的参数调用此成员函数的对象的地址作为一个隐含的参数传递给传递给this指针,即让指针,即让this指针指向调用此成员函数指针指向调用此成员函数的对象,从而使成员函数知道该对哪个对象进行操的对象,从而使成员函数知道该对哪个对象进行操作。作。v使用使用this指针,保证了每个对象可以拥有不同的数据成指针,保证了每个对象可以拥有不同的数据成员,但处理这些数据成员的代码可以被所有的对象共享。员,但处理这些数据成员的代码可以被所有的对象共享。26第26页,共87页,编辑于2022年,
16、星期一例2.2 类的应用举例(教材28页)一圆型游泳池如图所示,现需要在其周围建一一圆型游泳池如图所示,现需要在其周围建一圆型过道,并在其四周围上栅栏。栅栏价格为圆型过道,并在其四周围上栅栏。栅栏价格为35元元/米,过道造价为米,过道造价为20元元/平方米。过道宽度为平方米。过道宽度为3米,游泳池米,游泳池半径由键盘输入。要求编程计算并输出过道和栅栏半径由键盘输入。要求编程计算并输出过道和栅栏的造价。的造价。游泳池过道问题分析:问题分析:圆类及其应用圆类及其应用27第27页,共87页,编辑于2022年,星期一第1步定义圆类v/Circle.h文件文件 vconst double PI=3.14
17、159;vclass CCircle vvpublic:v CCircle(double r=1);vdouble Circumference();v/计算圆的周长计算圆的周长vdouble Area();/计算圆的面积计算圆的面积vvirtual CCircle()vprivate:v double radius;v;v /Circle.cpp文件文件#include Circle.hCCircle:CCircle(double r)radius=r0?r:1;double CCircle:Area()return PI*radius*radius;double CCircle:Circum
18、ference()return 2*PI*radius;28第28页,共87页,编辑于2022年,星期一第2步 使用圆类求解问题v/Ex2_2Main.cpp文件文件v#includev#include“Circle.h”vconst double FencePrice=35;/栅栏单价栅栏单价vconst double ConcretePrice=20;/过道单价过道单价vvoid main()vvdouble radius,FenceCost,ConcreteCost;vcoutradius;vCCircle Pool(radius);/声明声明 Circle 对象对象vCCircle P
19、oolRim(radius+3);v/计算栅栏造价并输出计算栅栏造价并输出vFenceCost=PoolRim.Circumference()*FencePrice;vcout 栅栏造价是栅栏造价是:¥FenceCost endl;v/计算过道造价并输出计算过道造价并输出vConcreteCost=(PoolRim.Area()-Pool.Area()*ConcretePrice;vcout 过道造价是过道造价是:¥ConcreteCost-)34第34页,共87页,编辑于2022年,星期一2.3 继承性和派生类vv继承是面向对象程序设计的最重要的特点之一,是继承是面向对象程序设计的最重要的特
20、点之一,是软件重用软件重用的一种重要形式,是对实际问题中的一种重要形式,是对实际问题中分层特性分层特性分层特性分层特性的一种自然描述。的一种自然描述。v继承的实质:继承的实质:是从已有的类建立新类是从已有的类建立新类。v通过继承,派生类自动拥有基类的所有成员(数据通过继承,派生类自动拥有基类的所有成员(数据成员和成员函数)成员和成员函数)vv基类和派生类基类和派生类vv单继承和多继承单继承和多继承单继承和多继承单继承和多继承 35第35页,共87页,编辑于2022年,星期一2.3.1 派生类的定义class 派生类名:派生类名:继承方式继承方式 基类名基类名 成员声明;成员声明;36第36页,
21、共87页,编辑于2022年,星期一继承方式三种继承方式:三种继承方式:public(公有)、(公有)、private(私有)、(私有)、protected(保(保护),护),表表2-1 2-1 继承方式对派生类的影响继承方式对派生类的影响继承方式继承方式说说 明明publicpublic(公有)(公有)基基类类的的publicpublic和和protectedprotected成成员员被被派派生生类类继继承承后后,保保持持原原来来的的访访问问属属性性不变不变privateprivate(私有)(私有)基基类类的的publicpublic和和protectedprotected成成员员被被派派生
22、生类类继继承承后后,变变成成派派生生类类的的privateprivate成员成员protectedprotected(保保护)护)基基类类的的publicpublic和和protectedprotected成成员员被被派派生生类类继继承承后后,变变成成派派生生类类的的protectedprotected成员成员37第37页,共87页,编辑于2022年,星期一派生类构成示意图:派生类构成示意图:派生类派生类派生类派生类成员成员成员成员基类部分基类部分基类部分基类部分新定义部分新定义部分新定义部分新定义部分私有成员私有成员保护成员保护成员公有成员公有成员私有成员私有成员保护成员保护成员公有成员公有
23、成员 每个派生类对象所占有的存储空间的大小等于其基类每个派生类对象所占有的存储空间的大小等于其基类每个派生类对象所占有的存储空间的大小等于其基类每个派生类对象所占有的存储空间的大小等于其基类部分的所有部分的所有部分的所有部分的所有数据成员数据成员数据成员数据成员占有的存储空间的大小与新定义部分的所有占有的存储空间的大小与新定义部分的所有占有的存储空间的大小与新定义部分的所有占有的存储空间的大小与新定义部分的所有数据成员数据成员数据成员数据成员占有的存储空间的大小的总和。占有的存储空间的大小的总和。占有的存储空间的大小的总和。占有的存储空间的大小的总和。38第38页,共87页,编辑于2022年,
24、星期一例2.3 类的派生示例教材31页 点-圆-圆柱体 1 定义基类定义基类CPointv /Point.h文件文件v#include vclass CPoint /基类基类CPointvvpublic:vCPoint(double xx=0,double yy=0);vvirtual CPoint();vdouble GetX()return X;vdouble GetY()return Y;vdouble area()return 0.0;vprivate:vdouble X,Y;v;v CPoint:CPoint(double xx,double yy)X=xx;Y=yy;coutCPo
25、int 类构造函数被调用类构造函数被调用n;CPoint:CPoint()cout0?r:1;coutCCircle 类构造函数被调用类构造函数被调用n;CCircle:CCircle()cout0?h:1;vcoutCCylinder 类构造函数被调用类构造函数被调用n;vvCCylinder:CCylinder()vv coutCCylinder 类的析构函数被调用类的析构函数被调用n;v42第42页,共87页,编辑于2022年,星期一派生类的使用v/Ex2_3Main.cpp文件文件v#include v#include Cylinder.hvvoid main()v v CCylind
26、er cy1(2,3,20,10);v /通过派生类对象访问基类公有成员通过派生类对象访问基类公有成员v coutcy1.GetX()cy1.GetY()vcy1.GetRadius()cy1.GetHeight()endl;v /访问基类和派生类中的同名成员访问基类和派生类中的同名成员v cout cy1.area()cy1.CCircle:area()cy1.CPoint:area()v endl;v43第43页,共87页,编辑于2022年,星期一2.3.2 派生类的构造函数与析构函数v基类的构造函数不能被继承,需要在派生类基类的构造函数不能被继承,需要在派生类中负责基类成员的初始化。中负
27、责基类成员的初始化。v通过在通过在成员初始化表成员初始化表中显式调用基类的构造函中显式调用基类的构造函数来实现的数来实现的v单一继承时的构造函数单一继承时的构造函数派生类名派生类名:派生类名派生类名(基类成员和本类成员所基类成员和本类成员所 需的形参需的形参):基类名基类名(实参实参)本类成员初始化;本类成员初始化;44第44页,共87页,编辑于2022年,星期一派生类构造函数与析构函数的执行顺序v在创建派生类的对象时,派生类的构造函数总是先调用其基类在创建派生类的对象时,派生类的构造函数总是先调用其基类的构造函数来完成基类成员的初始化。的构造函数来完成基类成员的初始化。v如果在基类和派生类中
28、都包含其他类的对象(即有对象成员),则如果在基类和派生类中都包含其他类的对象(即有对象成员),则在创建派生类的对象时,首先执行基类的对象成员的构造函数,接在创建派生类的对象时,首先执行基类的对象成员的构造函数,接着执行基类的构造函数,然后执行派生类的对象成员的构造函数,着执行基类的构造函数,然后执行派生类的对象成员的构造函数,最后才执行派生类的构造函数。最后才执行派生类的构造函数。v当派生类对象的生命周期结束时,析构函数的执行顺序和当派生类对象的生命周期结束时,析构函数的执行顺序和构造函数的执行顺序正好相反。构造函数的执行顺序正好相反。45第45页,共87页,编辑于2022年,星期一2.3.3
29、 多继承v如果一个派生类有多个直接基类,则称如果一个派生类有多个直接基类,则称为多继承。为多继承。v多继承意味着一个派生类可以继承多个多继承意味着一个派生类可以继承多个基类的成员,这种强大的功能支持了软基类的成员,这种强大的功能支持了软件的重用性,但可能会引起大量的二义件的重用性,但可能会引起大量的二义性(歧义性)问题。性(歧义性)问题。46第46页,共87页,编辑于2022年,星期一1.多继承的定义格式class class :,;v其中,继承方式的使用与单继承完全相同。其中,继承方式的使用与单继承完全相同。v【例例2.42.4】使用多继承使用多继承 (教材(教材3535页)页)47第47页
30、,共87页,编辑于2022年,星期一多继承举例vclass CBase1 vvpublic:vCBase1(int x=0)value=x;v int GetValue()const return value;vprotected:v int value;/保护成员,能被派生类直接访问保护成员,能被派生类直接访问v;vclass CBase2 vvpublic:vCBase2(int y=0)v value=y;v int GetValue()const v return value;vprotected:v int value;/保护成员,能被派生类直接访问保护成员,能被派生类直接访问v;c
31、lass CDerived:public CBase1,public CBase2 public:CDerived(int x=0,int y=0,int v=0):CBase1(x),CBase2(y)/基类成员的初始化基类成员的初始化 value=v;int GetData()return CBase1:value+CBase2:value;/访问不同基类的同名成员访问不同基类的同名成员 int Get Value()const return value;private:int value;48第48页,共87页,编辑于2022年,星期一派生类的使用vvoid main()vv CDeri
32、ved d(10,20,500);v coutCBase1的的value值:值:d.CBase1:GetValue()/访问基类的同访问基类的同名成员名成员v nCBase2的的value值:值:d.CBase2:GetValue()/访问基类的访问基类的同名成员同名成员v nCDerived的的value值:值:d.GetValue()/访问派生类的同访问派生类的同名成员名成员v nCBase1与与CBase2的的value值之和:值之和:d.GetData()endl;v49第49页,共87页,编辑于2022年,星期一2.多继承派生类对象的初始化 v多继承派生类对象的初始化与单继承类多继承
33、派生类对象的初始化与单继承类似,利用成员初始化值语法显式调用所似,利用成员初始化值语法显式调用所有直接基类的构造函数对派生类中的基有直接基类的构造函数对派生类中的基类成员进行初始化,这些构造函数之间类成员进行初始化,这些构造函数之间用逗号隔开。用逗号隔开。50第50页,共87页,编辑于2022年,星期一3.二义性问题1.1.因因不不同同基基类类中中可可能能含含有有同同名名成成员员引引起起的的二二义义性性:用用基基类类名名加加以以限限定定,形形如如:基基类类名名:同同名名成成员员名名,以明确指出所使用的成员是从哪个基类继承来的。以明确指出所使用的成员是从哪个基类继承来的。2.2.因因公公共共基基
34、类类使使得得派派生生类类中中含含有有同同名名成成员员,因因此此也也会会产产生生二二义义性性。为为了了消消除除这这种种二二义义性性,也也必必须须使使用用基基类名限定所访问的同名成员。类名限定所访问的同名成员。51第51页,共87页,编辑于2022年,星期一4.虚基类v解决多继承中因公共基类而产生的二义性问题。解决多继承中因公共基类而产生的二义性问题。v声明虚基类的一般格式为:声明虚基类的一般格式为:class :virtual ;v保保留留字字virtualvirtual和和继继承承方方式式的的相相对对位位置置无无关关紧紧要要,但但要要放放在在基基类类名名之之前前,并并且且virtualvirt
35、ual只只对对紧紧跟跟其其后后的的基基类名起作用。类名起作用。v【例例2.52.5】使用虚基类(教材使用虚基类(教材3636页)。页)。52第52页,共87页,编辑于2022年,星期一虚基类的定义方法v声明声明 class 派生类名:virtual public 基类名 ;v作用作用解决多继承时因公共基类而产生的二义性问题.为最新的派生类提供唯一的基类成员(数据),而不重复产生多次拷贝v注意:注意:在第一级继承时就要将公共基类设计为虚基类。53第53页,共87页,编辑于2022年,星期一vclass CBasevpublic:v CBase(int i)a=i;vprotected:v int
36、 a;v;vclass CDerived1:virtual public CBasevpublic:v CDerived1(int p1,int p2):CBase(p1)v d1=p2;vprotected:v int d1;v;Base Derived1 Derived2 DD class CDerived2:virtual public CBasepublic:CDerived2(int x1,int x2):CBase(x1)d2=x2;protected:int d2;使用虚基类避免二义性,保持a为一个值54第54页,共87页,编辑于2022年,星期一vclass CDD:CDeri
37、ved1,CDerived2vpublic:v CDD(int i1,int i2,int i3,int i4)v :CDerived1(i1,i2),CDerived2(i3,i4),CBase(i1)v v void printDD()constv /可以直接访问虚基类中的成员,不会产生二义性可以直接访问虚基类中的成员,不会产生二义性vcoutCDD类中的类中的a值值:aendl;v /访问从访问从Derived1中继承来的虚基类的成员中继承来的虚基类的成员vcoutCDD类中的类中的CDerived1:a值值:CDerived1:aendl;v /访问从访问从Derived2中继承来的虚
38、基类的成员中继承来的虚基类的成员vcoutCDD类中的类中的CDerived2:a值值:CDerived2:aendl;vcoutCDD类中的类中的d1值值:d1endlvCDD类中的类中的d2值值:d2endl;v v;55第55页,共87页,编辑于2022年,星期一vvoid main()v CDD dd(10,15,20,25);v dd.printDD();v coutendl;vv输出结果:输出结果:vCDD类中的类中的a值值:10vCDD类中的类中的Derived1:a值值:10vCDD类中的类中的Derived2:a值值:10vCDD类中的类中的d1值值:15vCDD类中的类中的
39、d2值值:2556第56页,共87页,编辑于2022年,星期一使用虚基类时应注意的问题:1.从从虚虚基基类类派派生生出出的的所所有有派派生生类类(直直接接派派生生类类和和间间接接派派生生类类),都都要要调调用用虚虚基基类类的的构构造造函函数数,以以初初始始化化虚虚基基类中的数据成员。类中的数据成员。2.基基类类构构造造函函数数的的调调用用顺顺序序是是,先先调调用用虚虚基基类类的的构构造造函函数数,然然后后再再调调用用非非虚虚基基类类的的构构造造函函数数,与与成成员员初初始始化化值值列列表表中的顺序无关。中的顺序无关。3.虚虚基基类类的的构构造造函函数数只只调调用用一一次次。虚虚基基类类的的数数
40、据据成成员员由由最最新新派派生生出出来来的的派派生生类类负负责责初初始始化化,并并且且只只被被初初始始化化一次。一次。57第57页,共87页,编辑于2022年,星期一2.4 多态性v在在面面向向对对象象的的程程序序设设计计中中,把把不不同同类类的的对对象象收收到到相相同同的的消消息息时时产产生生多多种种不不同同的的行为方式称为多态性。行为方式称为多态性。58第58页,共87页,编辑于2022年,星期一2.4.1 两种多态性 v联编:将一个函数调用链接上相应于函数体的代码,这一过程称联编:将一个函数调用链接上相应于函数体的代码,这一过程称为函数联编(简称联编)。为函数联编(简称联编)。v静态联编
41、:静态联编:在编译阶段完成;优点是执行速度快在编译阶段完成;优点是执行速度快v动态联编:动态联编:在运行时才能把函数调用与函数体联系在一起。在运行时才能把函数调用与函数体联系在一起。具有灵活性高,易于扩充和维护等优点,但运行效率较低。具有灵活性高,易于扩充和维护等优点,但运行效率较低。v静态联编所支持的多态性称为编译时的多态性,通过函数重载来实静态联编所支持的多态性称为编译时的多态性,通过函数重载来实现。现。v动态联编所支持的多态性称为运行时的多态性,通过虚函数来实现。动态联编所支持的多态性称为运行时的多态性,通过虚函数来实现。59第59页,共87页,编辑于2022年,星期一2.4.2 编译时
42、的多态性v【例例2.6】编译时的多态性编译时的多态性(教材(教材39页)页)60第60页,共87页,编辑于2022年,星期一静态联编时的几点说明通过例3-13可以看出:对基类和派生类中的同名成员函数(消息)的访问方式:1.通过各自的对象访问,结果正确多态;2.通过基类指针(或引用)指向派生类对象,访问同名成员函数时,结果与预想的不一致。总是调用在基类中定义的实现版总是调用在基类中定义的实现版本,本,没有实现多态没有实现多态。原因是对于普通成员函数的调用,是在编译时通过静态联编决定的。解决的办法是将同名消息定义为虚函数,动态联编。解决的办法是将同名消息定义为虚函数,动态联编。61第61页,共87
43、页,编辑于2022年,星期一2.4.3 虚函数v虚函数是实现动态联编的基础。虚函数是实现动态联编的基础。v虚函数的声明:在函数原型之前加虚函数的声明:在函数原型之前加virtual。class public:/虚函数的声明虚函数的声明 virtual (););/虚函数的定义虚函数的定义 :().62第62页,共87页,编辑于2022年,星期一应注意的问题:v应该在类层次结构中需要多态性的最高层应该在类层次结构中需要多态性的最高层类内声明虚函数。类内声明虚函数。v派生类中与基类虚函数原型完全相同的成派生类中与基类虚函数原型完全相同的成员函数,会自动成为虚函数。员函数,会自动成为虚函数。v不能把
44、静态成员函数、构造函数和全局函不能把静态成员函数、构造函数和全局函数声明为虚函数。数声明为虚函数。v析构函数可以声明为虚函数。析构函数可以声明为虚函数。v通过声明虚函数来使用通过声明虚函数来使用C+C+提供的多态性提供的多态性机制时,派生类应该从它的基类公有派生。机制时,派生类应该从它的基类公有派生。63第63页,共87页,编辑于2022年,星期一2.使用虚函数v必须合理调用虚函数才能实现动态联编。必须合理调用虚函数才能实现动态联编。v只有使用基类类型的指针或引用调用虚函数时,系统只有使用基类类型的指针或引用调用虚函数时,系统才以动态联编方式实现对虚函数的调用,才能获得运才以动态联编方式实现对
45、虚函数的调用,才能获得运行时的多态性。行时的多态性。v通常都用指向第一次定义虚函数的基类对象的指针或引通常都用指向第一次定义虚函数的基类对象的指针或引用来调用虚函数用来调用虚函数。v虚函数的使用(教材虚函数的使用(教材4141页)页)64第64页,共87页,编辑于2022年,星期一3.纯虚函数v纯虚函数:在声明时纯虚函数:在声明时“初始化值初始化值”为为0的函数。的函数。class class publicpublic:virtualvirtual ()=0=0;v纯虚函数不需要进行定义纯虚函数不需要进行定义65第65页,共87页,编辑于2022年,星期一抽象类v带有纯虚函数的类称为带有纯虚函
46、数的类称为抽象类抽象类。v不能声明抽象类的对象,但可以声明抽象类的指针就不能声明抽象类的对象,但可以声明抽象类的指针就引用。引用。v抽象类只能作为抽象类只能作为基类基类来使用。来使用。v一个类可以从基类一个类可以从基类继承接口继承接口和(或)和(或)继承实现继承实现v【例【例2.72.7】虚函数和纯虚函数的使用(教材】虚函数和纯虚函数的使用(教材4343页)页)Shape类的层次类的层次66第66页,共87页,编辑于2022年,星期一ShapePointCircleCylinder抽象基类,纯虚函数只声明,不用定义虚函数的声明,实现定义虚函数的声明,实现定义虚函数的声明,实现定义67第67页,
47、共87页,编辑于2022年,星期一vclass CShape vpublic:v virtual double area()const return 0.0;v virtual double volume()const return 0.0;v virtual void printShapeName()const=0;/纯纯虚函数虚函数v virtual void print()const=0;/纯虚函数纯虚函数v;68第68页,共87页,编辑于2022年,星期一vclass CPoint:public CShape vpublic:v CPoint(int=0,int=0);v virtua
48、l void printShapeName()const cout 点点:;v virtual void print()const;vprivate:v int x,y;/点的坐标点的坐标v;vCPoint:CPoint(int a,int b)v x=a;y=b;vvoid CPoint:print()const v cout x ,y ;69第69页,共87页,编辑于2022年,星期一vclass CCircle:public CPoint vpublic:v CCircle(double r=0.0,int x=0,int y=0);v double GetRadius()const;v
49、 virtual double area()const;v virtual void printShapeName()const cout 0?r:0;vdouble CCircle:GetRadius()const return radius;vdouble CCircle:area()const v return 3.14159*radius*radius;vvoid CCircle:print()constvv CPoint:print();v cout ;半径半径=radius;v70第70页,共87页,编辑于2022年,星期一vclass CCylinder:public CCirc
50、le vpublic:v CCylinder(double h=0.0,double r=0.0,int x=0,int y=0);v virtual double area()const;v virtual double volume()const;v virtual void printShapeName()const cout 0?h:0;vdouble CCylinder:area()const /圆柱体的表面积圆柱体的表面积v return 2*CCircle:area()+2*3.14159*GetRadius()*height;vdouble CCylinder:volume()