C++程序设计 大学基础教程第十一章.ppt

上传人:暗伤 文档编号:96549886 上传时间:2023-12-23 格式:PPT 页数:50 大小:233KB
返回 下载 相关 举报
C++程序设计 大学基础教程第十一章.ppt_第1页
第1页 / 共50页
C++程序设计 大学基础教程第十一章.ppt_第2页
第2页 / 共50页
点击查看更多>>
资源描述

《C++程序设计 大学基础教程第十一章.ppt》由会员分享,可在线阅读,更多相关《C++程序设计 大学基础教程第十一章.ppt(50页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、C+大学基础教程大学基础教程第第1111章章 多态性多态性北京科技大学北京科技大学信息基础科学系信息基础科学系2023/12/231北京科技大学多态性(Polymorphism)是面向对象程序设计的主要特征之一。多态性对于软件功能的扩展和软件重用都有重要的作用。是学习面向对象程序设计必须要掌握的主要内容之一。2第十一章第十一章 多态性多态性11.1 11.1 多态性的概念多态性的概念 11.2 11.2 继承中的静态联编继承中的静态联编 11.3 11.3 虚函数和运行时的多态虚函数和运行时的多态 11.4 11.4 纯虚函数和抽象类纯虚函数和抽象类 11.5 继承和派生的应用 11.6 模板

2、 311.1 11.1 多态性的概念多态性的概念2023/12/234北京科技大学11.1.1面向对象程序设计中多态的表现面向对象程序设计中多态的表现 总的来说,总的来说,不同对象不同对象对于对于相同的消息相同的消息有有不同的不同的响应响应,就是面向对象程序设计中的多态性。,就是面向对象程序设计中的多态性。具体在程序中,多态性有两种表现的方式:具体在程序中,多态性有两种表现的方式:n同一个对象调用名字相同、但是参数不同的函数,同一个对象调用名字相同、但是参数不同的函数,表现出不同的行为。在同一个类中定义的重载函数表现出不同的行为。在同一个类中定义的重载函数的调用,属于这种情况。的调用,属于这种

3、情况。n不同的对象调用名字和参数都相同的函数,表现出不同的对象调用名字和参数都相同的函数,表现出不同的行为。在派生类的应用中,经常会看到这样不同的行为。在派生类的应用中,经常会看到这样的调用。的调用。511.1.1面向对象程序设计中多态的表现面向对象程序设计中多态的表现面向对象程序设计中多态性表现为以下几种形面向对象程序设计中多态性表现为以下几种形式:式:n重载多态:通过调用相同名字的函数,表现出不同重载多态:通过调用相同名字的函数,表现出不同的行为。的行为。运算符重载也是一种重载多态。n运行多态:通过基类的指针,调用不同派生类的同运行多态:通过基类的指针,调用不同派生类的同名函数,表现出不同

4、的行为。许多面向对象程序设名函数,表现出不同的行为。许多面向对象程序设计的书籍中所说的多态性,就是这种多态。计的书籍中所说的多态性,就是这种多态。n模板多态,也称为参数多态:611.1.2 多态的实现:联编多态的实现:联编 一个具有多态性的程序语句一个具有多态性的程序语句,在执行的,在执行的时候,必须确定究竟是调用哪一个函数。时候,必须确定究竟是调用哪一个函数。也就是说,在执行的时候调用哪个函数也就是说,在执行的时候调用哪个函数是唯一地确定的。确定具有多态性的语是唯一地确定的。确定具有多态性的语句究竟调用哪个函数的过程称为句究竟调用哪个函数的过程称为联编联编(BindingBinding),)

5、,有的资料也翻译成有的资料也翻译成“绑定绑定”。711.1.2 多态的实现:联编多态的实现:联编联编有两种方式:联编有两种方式:静态联编静态联编和和动态联编动态联编。在在源程序编译的时候源程序编译的时候就能确定具有多态就能确定具有多态性的语句调用哪个函数,称为静态联编。性的语句调用哪个函数,称为静态联编。对于对于重载函数的调用重载函数的调用就是在编译的时候就是在编译的时候确定具体调用哪个函数,所以是属于静确定具体调用哪个函数,所以是属于静态联编。态联编。811.1.2 多态的实现:联编多态的实现:联编动态联编则是必须在程序运行时,才能够确定动态联编则是必须在程序运行时,才能够确定具有多态性的语

6、句究竟调用哪个函数。具有多态性的语句究竟调用哪个函数。用动态联编实现的多态,也称为用动态联编实现的多态,也称为运行时的多态运行时的多态。911.2 11.2 继承中的静态联编继承中的静态联编 2023/12/2310北京科技大学11.2.1派生类对象调用同名函数派生类对象调用同名函数 在派生类中可以定义和基类中同名的成员函数在派生类中可以定义和基类中同名的成员函数。这是对基类进行改造,为派生类增加新的行为这是对基类进行改造,为派生类增加新的行为的一种常用的方法。的一种常用的方法。通过不同的派生类的对象,调用这些同名的成通过不同的派生类的对象,调用这些同名的成员函数,实现不同的操作,也是多态性的

7、一种员函数,实现不同的操作,也是多态性的一种表现。表现。在程序编译的时候,就可以确定派生类对象具在程序编译的时候,就可以确定派生类对象具体调用哪个同名的成员函数。这是通过静态联体调用哪个同名的成员函数。这是通过静态联编实现的多态。编实现的多态。11 例例11.1 11.1 定定义义CircleCircle类类和和RectangleRectangle类类为为ShapeShape类类的的派派生生类类,通通过过CircleCircle类类和和RectangleRectangle类类的的对对象象调调用用重重载载函函数数getAreagetArea()()显显示示对象的面积。对象的面积。/例例11.1:

8、11.1:shape.hshape.h#ifndefifndef SHAPE_H SHAPE_H#define SHAPE_H#define SHAPE_Hclass Shapeclass Shape public:public:double double getAreagetArea()const;()const;void print()const;void print()const;/Shape/Shape类定义结束类定义结束12 class Circleclass Circle:public Shape :public Shape public:public:Circle(Circle(

9、intint=0,=0,intint=0,double=0.0);=0,double=0.0);double double getAreagetArea()const;()const;/返回面积返回面积void print()const;void print()const;/输出输出Circle Circle 类对象类对象private:private:intint x,y;x,y;/圆心座标圆心座标double radius;double radius;/圆半径圆半径;/派生类派生类CircleCircle定义结束定义结束class Rectangleclass Rectangle:publ

10、ic Shape :public Shape public:public:Rectangle(Rectangle(intint=0,=0,intint=0);=0);/构造函数构造函数double double getAreagetArea()const;()const;/返回面积返回面积void print()const;void print()const;/输出输出RectangleRectangle类对象类对象private:private:intint a,b;a,b;/矩形的长和宽矩形的长和宽;/派生类派生类RectangleRectangle定义结束定义结束#endifendif1

11、3/例例11.1:11.1:shape.shape.cppcpp#include#include using namespace std;using namespace std;#include shape.h#include shape.h double Shape:double Shape:getAreagetArea()const()const coutcout基类的基类的getAreagetArea函数,面积是函数,面积是;return 0.0;return 0.0;/Shape/Shape类类getAreagetArea函数的定义函数的定义void Shape:print()cons

12、tvoid Shape:print()const coutcoutBase class ObjectBase class Objectendlendl;/Shape/Shape类类printprint函数定义函数定义基类成员函数的定义基类成员函数的定义14 Circle:Circle(Circle:Circle(intint xValuexValue,intint yValueyValue,double,double radiusValueradiusValue)x=x=xValuexValue;y=;y=yValueyValue;radius=radius=radiusValueradius

13、Value;/Circle/Circle类构造函数类构造函数double Circle:double Circle:getAreagetArea()const()const coutcoutCircleCircle类的类的getAreagetArea函数,面积是函数,面积是;return 3.14159*radius*radius;return 3.14159*radius*radius;/Circle/Circle类类getAreagetArea函数定义函数定义void Circle:print()constvoid Circle:print()const coutcout center i

14、s;center is;coutcoutx=x y=y;x=x y=y;coutcout ;radius is radius ;radius is radiusendlendl;/Circle/Circle类类printprint函数定义函数定义CircleCircle类成员函数的定义类成员函数的定义15 Rectangle:Rectangle(Rectangle:Rectangle(intint aValueaValue,intint bValuebValue)a=a=aValueaValue;b=;b=bValuebValue;/Rectangle/Rectangle类构造函数类构造函数d

15、ouble Rectangle:double Rectangle:getAreagetArea()const()const coutcoutRectangleRectangle类的类的getAreagetArea函数,面积是函数,面积是;return a*b;return a*b;/Rectangle /Rectangle类类getAreagetArea函数定义函数定义void Rectangle:print()constvoid Rectangle:print()const coutcout highthight is a;is a;coutcoutwidth isbwidth isbend

16、lendl;/Rectangle/Rectangle类类printprint函数定义函数定义 RectangleRectangle类成员类成员函数的定义函数的定义16/例例11.1:11_1.11.1:11_1.cppcpp#include#include using std:using std:coutcout;using std:using std:endlendl;#include shape.h#include shape.h /包含头文件包含头文件void main()void main()Circle circle(22,8,3.5);/Circle circle(22,8,3.5

17、);/创建创建CircleCircle类对象类对象 Rectangle rectangle(10,10);/Rectangle rectangle(10,10);/创建创建RectangleRectangle类对象类对象 coutcout 调用的是调用的是;cout coutcircle.circle.getAreagetArea()()endlendl;/;/静态联编静态联编 coutcout 调用的是调用的是;cout coutrectangle.rectangle.getAreagetArea()()endlendl;/;/静态联编静态联编 例例11.111.1的主函数的主函数调用的是调

18、用的是Circle类的类的getarea函函数,面积是数,面积是38.4845 调用的是调用的是Ractangle类的类的getarea函数,面积是函数,面积是100 1711.2.1派生类对象调用同名函数派生类对象调用同名函数对于派生类对象调用成员函数,可以有对于派生类对象调用成员函数,可以有以下的结论:以下的结论:n派生类对象可以直接调用本类中与基类成员派生类对象可以直接调用本类中与基类成员函数同名的函数,不存在二义性;函数同名的函数,不存在二义性;n在编译时就能确定对象将调用哪个函数,属在编译时就能确定对象将调用哪个函数,属于静态联编,不属于运行时的多态。于静态联编,不属于运行时的多态。

19、1811.2.2通过基类指针调用同名函数通过基类指针调用同名函数 从继承的角度来看,派生类对象是基类对象的从继承的角度来看,派生类对象是基类对象的一个具体的特例。或者说,派生类对象是某一一个具体的特例。或者说,派生类对象是某一种特定类型的基类对象。种特定类型的基类对象。n例如,例如,CircleCircle类是类是ShapeShape类的公有继承,类的公有继承,“圆圆”是是“图形图形”的一种特例。或者说,圆是一种特定的图的一种特例。或者说,圆是一种特定的图形,具有图形的基本特征。形,具有图形的基本特征。但是,这种关系不是可逆的。不可以说基类的但是,这种关系不是可逆的。不可以说基类的对象具有派生

20、类对象的特征,基类对象也不是对象具有派生类对象的特征,基类对象也不是派生类对象的一个特例。派生类对象的一个特例。1911.2.2通过基类指针调用同名函数通过基类指针调用同名函数 在关于在关于基类对象基类对象和和派生类对象派生类对象的操作上,可以的操作上,可以允许以下的操作:允许以下的操作:n派生类对象可以赋值给基类对象;派生类对象可以赋值给基类对象;n派生类对象的地址可以赋值给基类对象的指针。或派生类对象的地址可以赋值给基类对象的指针。或者说,可以用派生类对象的地址初始化基类对象的者说,可以用派生类对象的地址初始化基类对象的指针;指针;n可以将基类对象的引用,定义为派生类对象的别名,可以将基类

21、对象的引用,定义为派生类对象的别名,或者说,用派生类对象初始化基类的引用。或者说,用派生类对象初始化基类的引用。n通过派生类对象的地址初始化的通过派生类对象的地址初始化的基类对象的指针基类对象的指针,可以可以访问基类的公有成员访问基类的公有成员,也可以,也可以访问和基类成员访问和基类成员函数同名的函数函数同名的函数。2011.2.2通过基类指针调用同名函数通过基类指针调用同名函数 以下这些操作是不可以进行的:以下这些操作是不可以进行的:n不可以将基类对象赋值给派生类对象;不可以将基类对象赋值给派生类对象;n不可以用基类对象的地址初始化派生类对象的不可以用基类对象的地址初始化派生类对象的指针;指

22、针;n不可以将派生类对象的引用定义为基类对象的不可以将派生类对象的引用定义为基类对象的别名;别名;n不可以通过用派生类对象初始化的不可以通过用派生类对象初始化的基类对象的基类对象的指针指针,访问,访问派生类新增加的派生类新增加的和基类公有成员不和基类公有成员不重名的重名的公有成员公有成员。21 例例11.2 11.2 在例在例11.111.1所定义的类的基础上,观察通过派生类对所定义的类的基础上,观察通过派生类对象地址初始化的基类对象的指针访问象地址初始化的基类对象的指针访问getAreagetArea函数的结果。函数的结果。#include include using namespace s

23、td;using namespace std;#include shape.h#include shape.h void main()void main()Shape*shape_ Shape*shape_ptrptr;Circle circle(22,8,3.5);Circle circle(22,8,3.5);Rectangle rectangle(10,10);Rectangle rectangle(10,10);shape_shape_ptrptr=&circle;=&circle;coutcout-getAreagetArea()()endlendl;/;/静态联编静态联编 shap

24、e_shape_ptrptr=&rectangle;=&rectangle;cout cout-getAreagetArea()()endlendl;/;/静态联编静态联编 circle circle 对对象象初初始始化化shape_shape_ptrptr指指针针访访问问的的getAreagetArea函数是函数是基类的基类的getAreagetArea函数,面积是函数,面积是 0 0rectangle rectangle 对对象象初初始始化化shape_shape_ptrptr指指针针访问的访问的getAreagetArea函数是函数是基类的基类的getAreagetArea函数,面积是函

25、数,面积是 0 02211.2.2通过基类指针调用同名函数通过基类指针调用同名函数 程序运行结果表明:程序运行结果表明:n确实可以用派生类对象的地址初始化基类对象的指确实可以用派生类对象的地址初始化基类对象的指针;针;n通过用派生类对象地址初始化的基类对象指针,只通过用派生类对象地址初始化的基类对象指针,只能调用基类的公有成员函数。在以上例子中,就是能调用基类的公有成员函数。在以上例子中,就是调用基类的调用基类的getAreagetArea函数,而不是派生类的函数,而不是派生类的getAreagetArea函数。函数。n这种调用关系的确定,也是在编译的过程中完成的,这种调用关系的确定,也是在编

26、译的过程中完成的,属于静态联编,而不属于运行时的多态。属于静态联编,而不属于运行时的多态。2311.3 11.3 虚函数和运行时的多态虚函数和运行时的多态 2023/12/2324北京科技大学11.3 虚函数和运行时的多态虚函数和运行时的多态通过指向基类的指针访问基类和派生类通过指向基类的指针访问基类和派生类的同名函数,是实现运行时的多态的必的同名函数,是实现运行时的多态的必要条件,但不是全部条件。要条件,但不是全部条件。除此以外,还必须将基类中的同名函数除此以外,还必须将基类中的同名函数定义为定义为虚函数虚函数。2511.3.1 虚函数虚函数虚函数可以在类的定义中声明函数原型的时候虚函数可以

27、在类的定义中声明函数原型的时候来说明,格式如下:来说明,格式如下:virtual virtual 函数名函数名(参数表参数表););n在函数原型中声明函数是虚函数后,具体定义这个在函数原型中声明函数是虚函数后,具体定义这个函数时就不需要再说明它是虚函数了。函数时就不需要再说明它是虚函数了。如果在基类中直接定义同名函数,定义虚函数如果在基类中直接定义同名函数,定义虚函数的格式是:的格式是:virtual virtual 函数名函数名(参数表参数表)2611.3.1 虚函数虚函数基类中的同名函数声明或定义为虚函数后,派基类中的同名函数声明或定义为虚函数后,派生类的同名函数无论是不是用生类的同名函数

28、无论是不是用virtualvirtual来说明,来说明,都将自动地成为虚函数。从程序可读性考虑,都将自动地成为虚函数。从程序可读性考虑,一般都会在这些函数的声明或定义时,用一般都会在这些函数的声明或定义时,用virtualvirtual来加以说明。来加以说明。只要对例只要对例11.211.2中的头文件稍加修改,也就是将中的头文件稍加修改,也就是将基类和派生类中的基类和派生类中的getAreagetArea函数都声明为虚函函数都声明为虚函数,再重新编译和运行程序,就可以得到运行数,再重新编译和运行程序,就可以得到运行时的多态的效果。时的多态的效果。27 例例11.3 11.3 将例将例11.21

29、1.2进行修改,使得程序具有运行时的多态的效果。进行修改,使得程序具有运行时的多态的效果。/例例11.3:11.3:shape1.hshape1.h#ifndefifndef SHAPE_H SHAPE_H#define SHAPE_H#define SHAPE_Hclass Shape class Shape public:public:virtualvirtual double double getAreagetArea()const;()const;void print()const;void print()const;/Shape/Shape类定义结束类定义结束28 class Cir

30、cle:public Shape class Circle:public Shape public:public:Circle(Circle(intint=0,=0,intint=0,double=0.0);=0,double=0.0);virtualvirtual double double getAreagetArea()const;()const;/返回面积返回面积void print()const;void print()const;/输出输出Circle Circle 类对象类对象t tprivate:private:intint x,y;x,y;/圆心座标圆心座标double ra

31、dius;double radius;/圆半径圆半径;/派生类派生类CircleCircle定义结束定义结束class Rectangle:public Shape class Rectangle:public Shape public:public:Rectangle(Rectangle(intint=0,=0,intint=0);=0);/构造函数构造函数 virtualvirtual double double getAreagetArea()const;()const;/返回面积返回面积void print()const;void print()const;/输出输出Rectangle

32、Rectangle类对象类对象private:private:intint a,b;a,b;/矩形的长和宽矩形的长和宽;/派生类派生类RectangleRectangle定义结束定义结束#endifendif29 例例11.2 11.2 在例在例11.111.1所定义的类的基础上,观察通过派生类对所定义的类的基础上,观察通过派生类对象地址初始化的基类对象的指针访问象地址初始化的基类对象的指针访问getAreagetArea函数的结果。函数的结果。#include include using namespace std;using namespace std;#include shape1.h#

33、include shape1.h void main()void main()Shape*shape_ Shape*shape_ptrptr;Circle circle(22,8,3.5);Circle circle(22,8,3.5);Rectangle rectangle(10,10);Rectangle rectangle(10,10);shape_shape_ptrptr=&circle;=&circle;coutcout-getAreagetArea()()endlendl;/;/动动态联编态联编 shape_shape_ptrptr=&rectangle;=&rectangle;c

34、out cout-getAreagetArea()()-getAreagetArea()()函数调用,当函数调用,当shape_shape_ptrptr指指针中是针中是CircleCircle类对象地址时,访问的是类对象地址时,访问的是CircleCircle类类的的getAreagetArea函数。而函数。而shape_shape_ptrptr指针中是指针中是RectangleRectangle类对象的地址时,访问的是类对象的地址时,访问的是RectangleRectangle类的类的getAreagetArea函数。函数。这种方式的函数调用,在编译的时候是不能确定这种方式的函数调用,在编译

35、的时候是不能确定具体调用哪个函数的。只有程序运行后,才能知具体调用哪个函数的。只有程序运行后,才能知道指针道指针shape_shape_ptrptr中存放的是什么对象的地址,然中存放的是什么对象的地址,然后再决定调用哪个派生类的函数。是后再决定调用哪个派生类的函数。是一种运行时一种运行时决定的多态性决定的多态性。3111.3.1 虚函数虚函数要实现运行时的多态,需要以下条件:要实现运行时的多态,需要以下条件:n必须通过指向基类对象的指针访问和基类成员必须通过指向基类对象的指针访问和基类成员函数同名的派生类成员函数;函数同名的派生类成员函数;n或者用派生类对象初始化的基类对象的引用访或者用派生类

36、对象初始化的基类对象的引用访问和基类成员函数同名的派生类成员函数;问和基类成员函数同名的派生类成员函数;n派生类的继承方式必须是派生类的继承方式必须是公有继承公有继承;n基类中的同名成员函数必须定义为虚函数。基类中的同名成员函数必须定义为虚函数。3211.3.2 虚函数的使用虚函数的使用虚函数必须正确的定义和使用。否则,即使在虚函数必须正确的定义和使用。否则,即使在函数原型前加了函数原型前加了virtualvirtual的说明,也可能得不的说明,也可能得不到运行时多态的特性。到运行时多态的特性。n必须首先在基类中声明虚函数。在多级继承的情况必须首先在基类中声明虚函数。在多级继承的情况下,也可以

37、不在最高层的基类中声明虚函数。例如下,也可以不在最高层的基类中声明虚函数。例如在第二层定义的虚函数,可以和第三层的虚函数形在第二层定义的虚函数,可以和第三层的虚函数形成动态联编。但是,一般都是在最高层的基类中首成动态联编。但是,一般都是在最高层的基类中首先声明虚函数。先声明虚函数。3311.3.2 虚函数的使用虚函数的使用n基类和派生类的同名函数,必须函数名、返回基类和派生类的同名函数,必须函数名、返回值、参数表全部相同,才能作为虚函数来使用值、参数表全部相同,才能作为虚函数来使用。否则,即使函数用否则,即使函数用virtualvirtual来说明,也不具有虚来说明,也不具有虚函数的行为。函数

38、的行为。n静态成员函数不可以声明为虚函数。构造函数静态成员函数不可以声明为虚函数。构造函数也不可以声明为虚函数。也不可以声明为虚函数。n析构函数可以声明为虚函数,即可以定义虚析析构函数可以声明为虚函数,即可以定义虚析构函数。构函数。34 例例11.4 11.4 虚虚函函数数的的正正确确使使用用。分分析析以以下下程程序序,编编译译时时哪哪个个语语句句会会出出现现错错误误?为为什什么么?将将有有错错误误的的语语句句屏屏蔽蔽掉掉以以后后,程程序序运运行行结结果果如如何何?其其中哪些调用是静态联编,哪些是动态联编?中哪些调用是静态联编,哪些是动态联编?#include#include.hclass B

39、Bclass BBpublic:public:virtual void vf1()virtual void vf1()coutcoutBB:vf1BB:vf1被调用被调用 n;n;virtual void vf2()virtual void vf2()coutcoutBB:vf2BB:vf2被调用被调用 n;n;void f()void f()coutcoutBB:fBB:f被调用被调用 n;n;class DD:public BBclass DD:public BBpublic:public:virtualvirtual void vf1()void vf1()coutcoutDD:vf1D

40、D:vf1被调用被调用 n;n;void vf2(void vf2(int int i)i)coutcoutiiendlendl;void f()void f()coutcoutDD:fnvf1();-vf1();bpbp-vf2();-vf2();bpbp-vf2(10);-vf2(10);bpbp-f();-f();将这个语句注释掉后,运将这个语句注释掉后,运行结果将显示:行结果将显示:DD:vf1DD:vf1被调用被调用BB:vf2BB:vf2被调用被调用BB:fBB:f被调用被调用函数调用函数调用bp-vf2(10);是错是错误的。因为派生类的误的。因为派生类的vf2函函数和基类的数和

41、基类的vf2函数的参数函数的参数不同,派生类的不同,派生类的vf2就不是就不是虚函数。虚函数。其中其中bp-vf1()调用是动态联编。调用是动态联编。bp-vf2()是静态联编。是静态联编。bp-f()也是静态联编。也是静态联编。3611.3.3 虚析构函数虚析构函数如果用如果用动态创建动态创建的派生类对象的地址初始化基的派生类对象的地址初始化基类的指针,创建的过程不会有问题:仍然是先类的指针,创建的过程不会有问题:仍然是先调用基类构造函数,再执行派生类构造函数。调用基类构造函数,再执行派生类构造函数。但是,在用但是,在用deletedelete运算符删除这个指针的时候,运算符删除这个指针的时

42、候,由于指针是指向基类的,通过静态联编,只会由于指针是指向基类的,通过静态联编,只会调用基类的析构函数,释放基类成员所占用的调用基类的析构函数,释放基类成员所占用的空间。而空间。而派生类成员所占用的空间将不会被释派生类成员所占用的空间将不会被释放放。37#include using namespace std;class Shape public:Shape()coutShape类构造函数被调用类构造函数被调用n;Shape()coutShape类析构函数被调用类析构函数被调用n;class Circle:public Shape public:Circle(int xx=0,int yy=0

43、,double rr=0.0)x=xx;y=yy;radius=rr;coutCircle类构造函数被调用类构造函数被调用n;Circle()coutCircle类析构函数被调用类析构函数被调用n;private:int x,y;double radius;void main()Shape*shape_ptr;shape_ptr=new Circle(3,4,5);delete shape_ptr;例例11.5 11.5 定义简定义简单的单的ShapeShape类和类和CircleCircle类,观察类,观察基类指针的创建基类指针的创建和释放时如何调和释放时如何调用构造函数和析用构造函数和析构

44、函数。构函数。程序运行后在屏幕上显示:程序运行后在屏幕上显示:ShapeShape类构造函数被调用类构造函数被调用CircleCircle类构造函数被调用类构造函数被调用ShapeShape类析构函数被调用类析构函数被调用3811.3.3 虚析构函数虚析构函数为了解决派生类对象释放不彻底的问题,必须为了解决派生类对象释放不彻底的问题,必须将基类的析构函数定义为虚析构函数。格式是将基类的析构函数定义为虚析构函数。格式是在析构函数的名字前添加在析构函数的名字前添加virtualvirtual关键字。函关键字。函数原型如下:数原型如下:virtual virtual Shape();Shape();

45、此时,无论派生类析构函数是不是用此时,无论派生类析构函数是不是用virtualvirtual来说明,也都是虚析构函数。来说明,也都是虚析构函数。再用再用delete shape_delete shape_ptrptr来释放基类指针时,就来释放基类指针时,就会通过动态联编调用派生类的析构函数。会通过动态联编调用派生类的析构函数。3911.3.3 虚析构函数虚析构函数将将例例11.511.5程程序序中中的的Shape析析构构函函数数作作以以上上修修改改后,运行的结果将是:后,运行的结果将是:Shape类构造函数被调用类构造函数被调用Circle类构造函数被调用类构造函数被调用Circle类析构函数

46、被调用类析构函数被调用Shape类析构函数被调用类析构函数被调用4011.4 11.4 纯虚函数和抽象类纯虚函数和抽象类 4111.4 纯虚函数和抽象类纯虚函数和抽象类在前面的几个例子中,基类在前面的几个例子中,基类ShapeShape本身并不是本身并不是一个具体的一个具体的“形状形状”的抽象,而是各种实际的的抽象,而是各种实际的“形状形状”的抽象。的抽象。在在C+C+中,对于那些在基类中不需要定义具体中,对于那些在基类中不需要定义具体的行为的函数,可以定义为的行为的函数,可以定义为纯虚函数。纯虚函数。对于那些只是反映一类事物公共特性的类,在对于那些只是反映一类事物公共特性的类,在C+C+中可

47、以定义为中可以定义为“抽象类抽象类”。4211.4 纯虚函数和抽象类纯虚函数和抽象类纯虚函数声明的格式是:纯虚函数声明的格式是:virtual virtual 函数名函数名(参数表参数表)=0;)=0;纯虚函数的声明和使用有以下的特点:纯虚函数的声明和使用有以下的特点:n纯虚函数一定是在基类中声明的。纯虚函数一定是在基类中声明的。n在多级继承的情况下,纯虚函数除了在最高层基类在多级继承的情况下,纯虚函数除了在最高层基类中声明外,也可以在较低层的基类中声明。中声明外,也可以在较低层的基类中声明。n纯虚函数是没有函数体的。函数体是用纯虚函数是没有函数体的。函数体是用“=0”“=0”来来代替了。代替

48、了。n纯虚函数是不可以被调用的。凡是需要被调用的函纯虚函数是不可以被调用的。凡是需要被调用的函数都不可以声明为纯虚函数。数都不可以声明为纯虚函数。4311.4 纯虚函数和抽象类纯虚函数和抽象类抽象类的定义是基于纯虚函数的。抽象类的定义是基于纯虚函数的。凡是带有一个或几个纯虚函数的类,就是抽象类。凡是带有一个或几个纯虚函数的类,就是抽象类。抽象类定义的一般形式是:抽象类定义的一般形式是:class class 类名类名 public:public:virtual virtual 函数名函数名(参数表参数表)=0;)=0;/其他函数的声明其他函数的声明;/./.;4411.4 纯虚函数和抽象类纯虚

49、函数和抽象类抽象类的定义和使用具有以下的特点:抽象类的定义和使用具有以下的特点:n抽象类是不可以实例化的,也就是不可以定义抽象抽象类是不可以实例化的,也就是不可以定义抽象类的对象。类的对象。n但是,可以定义抽象类的指针和抽象类的引用。目但是,可以定义抽象类的指针和抽象类的引用。目的是通过这些指针或引用访问派生类的虚函数,实的是通过这些指针或引用访问派生类的虚函数,实现运行时的多态。现运行时的多态。n如果抽象类的派生类中没有具体实现纯虚函数的功如果抽象类的派生类中没有具体实现纯虚函数的功能,这样的派生类仍然是抽象类。能,这样的派生类仍然是抽象类。n抽象类中除了纯虚函数外,还可以定义其他的非纯抽象

50、类中除了纯虚函数外,还可以定义其他的非纯虚函数。虚函数。4511.4 纯虚函数和抽象类纯虚函数和抽象类虚函数、纯虚函数、多态性在面向对象程序设虚函数、纯虚函数、多态性在面向对象程序设计中有很大的作用。可以增强程序的通用性、计中有很大的作用。可以增强程序的通用性、可扩展性和灵活性。可扩展性和灵活性。46/例例11.6:11.6:shape2.hshape2.h#ifndefifndef SHAPE_H SHAPE_H#define SHAPE_H#define SHAPE_H class Shape class Shape/基类基类ShapeShape的定义的定义public:public:vi

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 技术方案

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁