第03章 C++面向对象.ppt

上传人:hyn****60 文档编号:70381249 上传时间:2023-01-19 格式:PPT 页数:73 大小:345.50KB
返回 下载 相关 举报
第03章 C++面向对象.ppt_第1页
第1页 / 共73页
第03章 C++面向对象.ppt_第2页
第2页 / 共73页
点击查看更多>>
资源描述

《第03章 C++面向对象.ppt》由会员分享,可在线阅读,更多相关《第03章 C++面向对象.ppt(73页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、第第3章章C+面向对象程序设计面向对象程序设计3.1类和对象类和对象作业作业13.2继承和派生类继承和派生类作业作业23.3多态和虚函数多态和虚函数3.4运算符重载运算符重载作业作业33.5输入输出流库输入输出流库作业作业43.1类和对象类和对象3.1.1类的定义类的定义定义类的一般格式如下:class private:public:;类中的数据和函数是类的成员数据和函数是类的成员,分别称为数据成员和成员函数数据成员和成员函数。public类成员,是公有的,能被外面的程序访问是公有的,能被外面的程序访问;private类成员,是私有的,只只能能由由类类中中的的函函数数所所使使用用,而而不不能能

2、被被外外面的程序所访问。面的程序所访问。是类定义中的实现部分,这部分包含所有在类体中声明的函数的定义。当类的成员函数的函数体在类的外部定义时,必须由作用域运算符作用域运算符“:”:”来通知编译系统该函数所属的类。3.1类和对象类和对象定义类时应注意:定义类时应注意:(1)(1)不不允允许许对对所所定定义义的的数数据据成成员员进进行行初初始始化化,例如类CMeter中,下面的定义是错误的:class CMeter.private:int m_nPos=10;.;(2)(2)在“public:”或“private:”后面定义的所有成员都是公有或私有的,直到下一个“public:”或“private

3、:”出现为止。(3)3)关键字关键字publicpublic和和privateprivate可以在类中出现多次可以在类中出现多次,且前后的顺序没有关系;(4)(4)关键字protected(保护)也可修饰成员的类型,它与private两者基本相似,但在类的继承时有所不同。(5)(5)数数据据成成员员的的类类型型可可以以是是整整型型、浮浮点点型型、字字符符型型、数数组组、指指针针和和引引用用等等,也也可可以是另一个类的对象。以是另一个类的对象。(6)(6)尽量将类单独存放在一个文件中或将类类的的声声明明放放在在.h文文件件中而将将成成员员函函数数的实现放在与的实现放在与.h文件同名的文件同名的.

4、cpp文件中文件中。3.1类和对象类和对象3.1.2对象的定义对象的定义定义对象,格式:定义的对象既可是一个普通对象,也可是一个数组对象或指针对象。对象的成员就是该对象的类所定义的成员。对象成员有数据成员和成员函数,其表示方式如下:.()前者用来表示数据成员,后者用来表示成员函数。“.”是一个运算符,用来表示对象的成员。指向对象的指针的成员表示如下:-()“-”与“.”运算符的区别是:“-”用来表示指向对象的指针成员,“.”用来表示一般对象成员。前者表示数据成员,后者表示成员函数。下面的两种表示是等价的:-(*).这对于成员函数也适用。另外,引用对象的成员表示与一般对象的成员表示相同。3.1类

5、和对象类和对象3.1.3构造函数和析构函数构造函数和析构函数构造函数构造函数为了能给数据成员自动设置某些初始值,就要使用类的特殊成员函数构造函数。构造函数的最大特点是在对象建立时它会被自动执行,用于变量、对象的初始化代码一般放在构造函数中。构造函数必须与相应的类同名,可以带参数,也可以不带参数,可以重载。例如:class CMeterpublic:CMeter(int nPos=10)/构造函数m_nPos=nPos;.构造函数CMeter(int nPos=10)中,nPos被设置了10,构造函数中相应实参没有被指定时,使用此缺省值。由于构造函数的参数只能在定义对象时指定。CMeter oM

6、eter;和CMeter oMeter(10);3.1类和对象类和对象析构函数析构函数与构造函数相对应的是析构函数。析构函数是另一种特殊的C+成员函数,它只是在类名称前面加上一个“”符号。每一个类只有一个析构函数,没有任何参数,也不返回任何值。例如:class CMeterpublic:.CMeter()/析构函数.析构函数一般在下列两种情况下被自动调用:(1)(1)当对象定义在一个函数体中,函数调用结束后,析构函数被自动调用当对象定义在一个函数体中,函数调用结束后,析构函数被自动调用。(2)(2)new为对象分配动态内存,用为对象分配动态内存,用delete释放对象时,析构函数被自动调用释放

7、对象时,析构函数被自动调用。3.1类和对象类和对象默认构造函数和析构函数默认构造函数和析构函数 类定义时,如果没有定义任何构造函数,编译器自动生成一个不带参数的默认构造函数,格式如下::()按构造函数的规定,默认构造函数名同类名。默认构造函数的这样格式也可由用户定义在类体中。在程序中定义一个对象而没有指明初始化时,则编译器便按默认构造函数来初始化该对象。默认构造函数对对象初始化时,则将对象的所有数据成员都初始化为零或空。如果一个类中没有定义析构函数,编译系统生成一个默认析构函数,格式::()默认析构函数名与该类的类名同名。是一个空函数。3.1类和对象类和对象构造函数的重载构造函数的重载 例例E

8、x_Ex_ConDefaultConDefault 带默认参数的构造函数。#include class CDatepublic:CDate(int year=2002,int month=7,int day=30)nYear=year;nMonth=month;nDay=day;coutnYear-nMonth-nDayendl;/其他公共成员private:int nYear,nMonth,nDay;void main()CDate day1;CDate day2(2002,8);运行结果为:2002-7-302002-8-303.1类和对象类和对象拷贝构造函数拷贝构造函数 例例Ex_Ex_

9、ConCopyConCopy 拷贝构造函数的使用。#include class CDatepublic:CDate(int year=2002,int month=7,int day=30)cout调用构造函数endl;nYear=year;nMonth=month;nDay=day;coutnYear-nMonth-nDayendl;CDate(const CDate&ymd)/*拷贝构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的参数(对象的引用)是不可变的(const类型)。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函

10、数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用.*/cout调用拷贝构造函数endl;coutymd.nYear-ymd.nMonth-ymd.nDayendl;private:int nYear,nMonth,nDay;void main()CDate day1(2002,8);CDate day2(day1);3.1类和对象类和对象 例例Ex_Ex_ConCopyDefaultConCopyDefault 默认拷贝构造函数的使用。#include class CDatepublic:CDate(int year=2002,int mon

11、th=7,int day=30)cout调用构造函数endl;nYear=year;nMonth=month;nDay=day;void output()coutnYear-nMonth-nDayendl;private:int nYear,nMonth,nDay;void main()CDate day1(2002,8);CDate day2(day1);/调用默认的拷贝函数day1.output();day2.output();运行结果为:调用构造函数2002-8-302002-8-30 3.1类和对象类和对象3.1.4对象成员初始化对象成员初始化在实际应用中往往需要多个类,这时就可能把一

12、个已定义类的对象作为另一个类的成员。为了能对这些对象成员进行初始化,构造函数定义格式::(形参表):对象1(参数表),对象2(参数表),对象n(参数表)对象1、对象2、对象n就是该类使用的其他类的对象,冒号“:”后面的列表称为成员初始化列表。说明:说明:(1)(1)对象成员必须初始化,但不能将对象成员直接在构造函数体内进行初始化。(2)(2)对象成员初始化时,必须有相应的构造函数,且多个对象成员的构造次序不是按初始化成员列表的顺序,而是按各类声明的先后次序进行的。(3)(3)对象成员初始化也可在类的外部进行,但必须与构造函数在一起。(4)(4)事实上,成员初始化列表也可用于类中的普通数据成员的

13、初始化。3.1类和对象类和对象3.1.5静态成员静态成员静态数据成员静态数据成员是同一个类中所有对象共享的成员,不是某一对象的成员。用静态数据成员可以节省内存,是所有对象所公有的,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,它的值是可以更新的。静态数据成员是静态存储的,具有静态生存期。定义静态数据成员:(1)(1)使用关键词使用关键词staticstatic声明静态数据成员声明静态数据成员。(2)(2)对对静静态态数数据据成成员员进进行行初初始始化化。静静态态数数据据成成员员要要分分配配空空间间,不不能能在在类类声声明明中中进进行初始化。静态数据

14、成员初始化在类的外部进行,它的格式如下:行初始化。静态数据成员初始化在类的外部进行,它的格式如下::=静态成员函数静态成员函数 静态成员函数属于类的静态成员,不是对象成员。对静态成员的引用不需要用对象名。在静态成员函数的实现中不不能能直直接接引引用用类类中中说说明明的的非非静静态态成成员员,可可以以引引用用类类中中说说明明的的静静态态成成员员。如果静态成员函数中要引用非静态成员时,可通过对象来引用。公有的静态成员函数既可以有通过相应的对象访问,也可以通过其所属的类名来引用。3.1类和对象类和对象3.1.6友元友元 例例Ex_Ex_FriendFuncFriendFunc 友元函数的使用。#in

15、clude class CPointpublic:CPoint()m_x=m_y=0;CPoint(unsigned x,unsigned y)m_x=x;m_y=y;void Print()cout Point(m_x ,m_y )endl;friend CPoint Inflate(CPoint&pt,int nOffset);/声明一个友元函数private:unsigned m_x,m_y;CPoint Inflate(CPoint&pt,int nOffset)/友元函数的定义 CPoint ptTemp=pt;ptTemp.m_x+=nOffset;/直接改变私有数据成员m_x和m

16、_y ptTemp.m_y+=nOffset;return ptTemp;void main()CPoint pt(10,20);pt.Print();pt=Inflate(pt,3);/调用友元函数pt.Print();3.1类和对象类和对象3.1.7常类型常类型常对象常对象常对象是指对象常量,定义格式:const 定定义义常常对对象象要要进进行行初初始始化化,该该对对象象不不能能再再被被更更新新,修修饰饰符符constconst可可以以放放在在类类名名后后面,也可以放在类名前面。面,也可以放在类名前面。常指针和常引用常指针和常引用常指针也是使用关键字const来修饰的。有三种形式。第一种形

17、式是将第一种形式是将const放在指针变量的类型之前,表示声明一个指向常量的放在指针变量的类型之前,表示声明一个指向常量的指针。指针。此时,在程序中不能通过指针来改变它所指向的数据值,但可以改变指针本身的值。第二种形式是将第二种形式是将const放在指针定义语句的指针名前,表示指针本身是一个放在指针定义语句的指针名前,表示指针本身是一个常量,称为指针常量或常指针。常量,称为指针常量或常指针。因此,不能改变这种指针变量的值,但可以改变指变量所指向的数据值。第三种形式是将第三种形式是将const在上述两个地方都加,表示声明一个指向常量的指针在上述两个地方都加,表示声明一个指向常量的指针常量,指针本

18、身的值不可改变,常量,指针本身的值不可改变,而且它所指向的数据的值也不能通过指针改变。3.1类和对象类和对象 例例Ex_Ex_ConstParaConstPara 常参数的函数传递。#include class COnepublic:void print(const int*p,int n)/使用常参数,指向常量的指针cout*p;for(int i=1;in;i+)cout,*(p+i);coutendl;private:;void main()int array6=1,2,3,4,5,6;COne one;one.print(array,6);3.1类和对象类和对象常成员函数常成员函数 例

19、例Ex_Ex_ConstFuncConstFunc 常成员函数的使用。#include class COnepublic:COne(int a,int b)x=a;y=b;void print();void print()const;/声明常成员函数private:int x,y;void COne:print()coutx,yendl;void COne:print()constcout使用常成员函数:x,yendl;void main()COne one(5,4);one.print();const COne two(20,52);two.print();3.1类和对象类和对象常成员函数可

20、以理解成是“只读”函数,既不能更改数据成员的值,也不能调用那些引起数据成员值变化的成员函数,只能调用const成员函数。例如:class CDatepublic:CDate(int mn,int dy,int yr);/构造函数int getMonth()const;/常成员函数void setMonth(int mn);/一般成员函数int month;/数据成员;int CDate:getMonth()const return month;/不能修改数据成员的值,只有一个返回值void CDate:setMonth(int mn)month=mn;/可以使用赋值等语句,修改数据成员的值3.

21、1类和对象类和对象常数据成员常数据成员类型修饰符const不仅可以说明成员函数,也可以说明数据成员。类中声明const数据成员时,只能通过成员初始化列表的方式来生成构造函数对数据成员初始化。例如:例例Ex_Ex_ConstDataConstData 常数据成员的使用。#include class COnepublic:COne(int a):x(a),r(x)/常数据成员的初始化 void print();const int&r;/引用类型的常数据成员private:const int x;/常数据成员static const int y;/静态常数据成员;const int COne:y=1

22、0;/静态数据成员的初始化void COne:print()coutx=x,y=y,r=rendl;void main()COne one(100);one.print();该程序的运行结果为:x=100,y=10,r=1003.1类和对象类和对象3.1.8this指针指针对一个对象调用成员函数时,编译器将对象的地址赋给this指针,再调用成员函数,每次成员函数存取数据成员时,由隐含作用this指针。可以用*this来标识调用该成员函数的对象。例例Ex_ThisEx_This this指针的使用。#include class COnepublic:COne()x=y=0;COne(int a,

23、int b)x=a;y=b;void copy(COne&a);/对象引用作函数参数 void print()coutx,yM这样的表达式中,其中pa是一个指向A类对象的指针。不同存储的对象生存期不同。所谓对象的生存期是指对象从被指对象从被创建开始到被释放为止的时间。创建开始到被释放为止的时间。按生存期的不同对象分为三种:(1)(1)局部对象局部对象:对象被定义时调用构造函数,该对象被创建,当程序退出定义该对象所在的函数体或程序块时,调用析构函数,释放该对象。(2)(2)静态对象静态对象:当程序第一次执行所定义的静态对象时,该对象被创建,当程序结束时,该对象被释放。(3)(3)全局对象全局对象

24、:当程序开始时,调用构造函数创建该对象,当程序结束时调用析构函数释放该对象。局部对象被定义在函数体或程序块内,作用域小,生存期短。静态对象被定义在文件中,作用域比较大,生存期也比较大。全局对象被定义在某个文件中,作用域在包含该文件的整个程序中,作用域是最大的,生存期也是最长的。3.1类和对象类和对象2.对象的生存期对象的生存期作业作业P368370:118偶数题偶数题3.2继承和派生类继承和派生类3.2.1单继承单继承公有继承公有继承(public)公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的。例如:class CStick:p

25、ublic CMeterintm_nStickNum;/声明一个私有数据成员public:void DispStick();/声明一个公有成员函数;/注意分号不能省略void CStick:DispStick()m_nStickNum=GetPos();/调用基类CMeter的成员函数coutm_nStickNum;从基类CMeter派生的CStick类除具有CMeter所有公有成员和保护成员外,还有自身的私有数据成员m_nStickNum和公有成员函数DispStick()。注意:注意:派生类中或派生类的对象可以使用基类的公有成员(包括保护成员),例如CStick的成员函数DispStick

26、中调用了基类CMeter的GetPos函数,oStick对象调用了基类的StepIt成员函数;但基类或基类的对象却不可以使用派生类的成员。3.2继承和派生类继承和派生类私有继承私有继承(private)私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。例例Ex_Ex_ClassPrivateDerivedClassPrivateDerived 派生类的私有继承示例。#include class CMeterpublic:CMeter(int nPos=10)m_nPos=nPos;CMeter()void StepIt()m_nPos+;int

27、GetPos()return m_nPos;protected:void SetPos(int nPos)m_nPos=nPos;private:int m_nPos;3.2继承和派生类继承和派生类class CStick:private CMeter/从CMeter派生,私有继承intm_nStickNum;/声明一个私有数据成员public:void DispStick();/声明一个公有成员函数void SetStick(int nPos)SetPos(nPos);int GetStick()return GetPos();void CStick:DispStick()m_nStickN

28、um=GetPos();/调用基类CMeter的成员函数coutm_nStickNum;void main()CMeter oMeter(20);CStick oStick;coutCMeter:oMeter.GetPos(),CStick:oStick.GetStick()endl;oMeter.StepIt();coutCMeter:oMeter.GetPos(),CStick:oStick.GetStick()endl;oStick.DispStick();3.2继承和派生类继承和派生类保护继承保护继承(protected)特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只

29、能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。注意,注意,一定要区分清楚派生类的对象和派生类中的成员函数对基类的访问是不同的。例如,在公有继承时,派生类的对象可以访问基类中的公有成员,派生类的成员函数可以访问基类中的公有成员和保护成员。在私有继承和保护继承时,基类的所有成员不能被派生类的对象访问,而派生类的成员函数可以访问基类中的公有成员和保护成员。3.2继承和派生类继承和派生类3.2.2派生类的构造函数和析构函数派生类的构造函数和析构函数 派派生生类类的的构构造造函函数数和和析析构构函函数数被被执执行行时时,基基类类相相应应的的构构造造函函数数和和析析构构函函数数也会被执行。

30、也会被执行。注意,派派生生类类对对象象在在建建立立时时,先先执执行行基基类类的的构构造造函函数数,然然后后执执行行派派生生类类的的构构造造函函数数。但但对对于于析析构构函函数数来来说说,其其顺顺序序刚刚好好相相反反,先先执执行行派派生生类类的的析析构构函函数,而后执行基类的析构函数。数,而后执行基类的析构函数。需要注意的是,如果在对派生类进行初始化时,需要对其基类设置初值,则可按下列格式进行:(总参表):(参数表1),(参数表2),(参数表n),对象成员1(对象成员参数表1),对象成员2(对象成员参数表2),对象成员n(对象成员参数表n).构造函数总参表后面给出的是需要用参数初始化的基类名、对

31、象成员名及各自对应的参数表,基基类类名名和和对对象象成成员员名名之之间间的的顺顺序序可可以以是是任任意意的的,且对于使用默认构造函数的基类和对象成员,可以不列出基类名和对象成员名。这里所说的对象成员是指在派生类中新声明的数据成员,它属于另外一个类的对象。对象成员必须在初始化列表中进行初始化。3.2继承和派生类继承和派生类3.2.3多继承多继承在类的继承中,允许一个派生类继承多个基类,这种多继承的方式可使派生类具有多个基类的特性,大大提高了程序代码的可重用性。多继承下派生类的定义是按下面的格式:class :,.;其中的继承方式还是前面的三种:public、private和protected。例

32、如:class A.class B.class C:public A,private B.派生类C的成员包含了基类A中成员和B中成员以及该类本身的成员。允许一个基类有多个派生类以及从一个基类的派生类中再进行多个层次的派生。3.2继承和派生类继承和派生类3.2.4虚基类虚基类 例例Ex_Conflict Ex_Conflict 基类成员调用的二义性。#include class Apublic:int x;A(int a=0)x=a;class B1:public Apublic:int y1;B1(int a=0,int b=0):A(b)y1=a;class B2:public Apubli

33、c:int y2;B2(int a=0,int b=0):A(b)y2=a;3.2继承和派生类继承和派生类class C:public B1,public B2public:int z;C(int a,int b,int d,int e,int m):B1(a,b),B2(d,e)z=m;void print()coutx=xendl;/编译出错的地方couty1=y1,y2=y2endl;coutz=zendl;void main()C c1(100,200,300,400,500);c1.print();3.2继承和派生类继承和派生类派生类B1和B2都从基类A继承,这时在派生类中就有两个基

34、类A的拷贝。当编译器编译到“cout”x=“xendl;”语句时,因无法确定成员x是从类B1中继承来的,还是从类B2继承来,产生了二义性,从而出现编译错误。解决这个问题的方法之一是使用域作用运算符“:”来消除二义性,例如若将print()函数实现代码变为:void print()coutB1:x=B1:xendl;coutB2:x=B2:xendl;couty1=y1,y2=y2endl;coutz=zendl;重新运行,结果为:B1:x=200B2:x=400y1=100,y2=300z=500 使用虚基类的目的是在多重派生的过程中,使公有的基类在派生类中只有一个拷贝,从而解决上述这种二义性

35、问题。3.2继承和派生类继承和派生类 例例Ex_Ex_VirtualBaseVirtualBase 虚基类的使用。#include class Apublic:int x;A(int a=0)x=a;class B1:virtualvirtual public Apublic:int y1;B1(int a=0,int b=0):A(b)y1=a;void print(void)coutB1:x=x,y1=y1endl;class B2:virtualvirtual public Apublic:int y2;B2(int a=0,int b=0):A(b)y2=a;void print(vo

36、id)coutB2:x=x,y2=y2endl;3.2继承和派生类继承和派生类class C:public B1,public B2public:int z;C(int a,int b,int d,int e,int m):B1(a,b),B2(d,e)z=m;void print()B1:print();B2:print();coutz=zendl;void main()C c1(100,200,300,400,500);c1.print();c1.x=400;c1.print()运行结果为:B1:x=0,y1=100B2:x=0,y2=300z=500B1:x=400,y1=100B2:x

37、=400,y2=300z=5003.2继承和派生类继承和派生类从程序中可以看出:(1)(1)声明一个虚基类的格式如下:virtual 声明虚基类与声明派生类一道进行,写在派生类名的后面。(2)(2)在派生类B1和B2中只有基类A的一个拷贝,改变成员x的值时,由基类B1和B2中的成员函数输出的成员x的值是相同的。(3)(3)由虚基类经过一次或多次派生出来的派生类,在其每一个派生类的构造函数的成员初始化列表中必须给出对虚基类的构造函数的调用,如果未列出,则调用虚基类的默认构造函数,在这种情况下,虚基类的定义中必须要有默认的构造函数。(4)(4)程序中,类C的构造函数尽管分别调用了其基类B1和B2的

38、构造函数,由于虚基类A在类C中只有一个拷贝,所以编译器无法确定应该由类B1的构造函数还是由类B2的构造函数来调用基类A的构造函数。在这种情况下,在执行类B1和B2的构造函数都不调用虚基类A的构造函数,而是在类C的构造函数中直接调用虚基类A的默认构造函数。若将A的构造函数改为:A(int a=100)x=a;则成员x的初始值为100。当然,不能变成:A(int a)x=a;因为类A中没有声明默认构造函数,因此会出现编译错误。可以是:A(int a)x=a;A():x(0)作业作业P370372:1927偶数题偶数题P397:实验实验53.3多态和虚函数多态和虚函数3.3.1虚函数虚函数 例例Ex

39、_Ex_VirtualFuncVirtualFunc 虚函数的使用。#include class CShapepublic:virtual float area()/将area定义成虚函数return 0.0;class CTriangle:public CShapepublic:CTriangle(float h,float w)H=h;W=w;float area()return(float)(H*W*0.5);private:float H,W;3.3多态和虚函数多态和虚函数class CCircle:public CShapepublic:CCircle(float r)R=r;flo

40、at area()return(float)(3.14159265*R*R);private:float R;void main()CShape*s2;s0=new CTriangle(3,4);coutarea()endl;s1=new CCircle(5);coutarea()endl;运行结果为:678.5398 3.3多态和虚函数多态和虚函数说明:说明:(1)(1)虚函数在重新定义时参数的个数和类型必须和基类中的虚函数完全匹配。(2)(2)虚函数所具备的上述功能,只有通过基类指针才可得到。虚函数在用对象名和成员运算符以正常方式调用时,不能达到其功能。例如:CShape ss;ss.ar

41、ea();将得到0.0。(3)(3)如果不使用new来创建相应的派生类对象,也可使用下列方法来实现:void main()CShape*p1,*p2;CTriangle tri(3,4);CCircle cir(5);p1=&tri;p2=○coutarea()endl;coutarea()endl;(4)(4)虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态的成员函数。(5)(5)可把析函数定义为虚函数,但不能将构造函数定义为虚函数。通常在释放基类中和其派生类中的动态申请的存储空间时,也要把析构函数定义为虚函数,以便实现撤消对象时的多态性。3.3多态和虚函数多态和虚函数3.

42、3.2纯虚函数和抽象类纯虚函数和抽象类 定义一个基类时,有时会遇到情况:无法定义基类中虚函数的具体实现,其实现完全依赖于其不同的派生类。例如,一个“形状类”由于没有确定的具体形状,因此其计算面积的函数也就无法实现。这时可将基类中的虚函数声明为纯虚函数。声明纯虚函数的一般格式为:声明纯虚函数的一般格式为:virtual virtual()=0)=0;与与一一般般虚虚函函数数不不同同的的是是:在在纯纯虚虚函函数数的的形形参参表表后后面面多多了了个个“=0 0”。把把函函数数名名赋赋于于0 0,本本质质上上是是将将指指向向函函数数的的指指针针的的初初值值赋赋为为0 0。虚虚函函数数不不能能有有具具体

43、体的的实实现现代码。代码。抽象类是指至少包含一个纯虚函数的特殊的类。它本身不能被实例化,也就抽象类是指至少包含一个纯虚函数的特殊的类。它本身不能被实例化,也就是说不能声明一个抽象类的对象。必须通过继承得到派生类后,在派生类中是说不能声明一个抽象类的对象。必须通过继承得到派生类后,在派生类中定义了纯虚函数的具体实现代码,才能获得一个派生类的对象。定义了纯虚函数的具体实现代码,才能获得一个派生类的对象。例例Ex_Ex_VirtualFuncVirtualFunc 虚函数的使用。#include class CShapepublic:virtual float area()=0;/将area定义成纯

44、虚函数;3.3多态和虚函数多态和虚函数class CTriangle:public CShapepublic:CTriangle(float h,float w)H=h;W=w;float area()/在派生类定义纯虚函数的具体实现代码return(float)(H*W*0.5);private:float H,W;class CCircle:public CShapepublic:CCircle(float r)R=r;float area()/在派生类定义纯虚函数的具体实现代码return(float)(3.14159265*R*R);private:float R;3.3多态和虚函数多

45、态和虚函数void main()CShape*pShape;CTriangle tri(3,4);couttri.area()endl;pShape=&tri;coutarea()endl;CCircle cir(5);coutcir.area()endl;pShape=○coutarea()endl;运行结果为:6678.539878.5398 3.4运算符重载运算符重载3.4.1运算符重载的语法运算符重载的语法为了重载运算符,必须定义一个特殊的函数,以便通知编译器,遇到该重载运算符时调用该函数,并由该函数来完成该运算符应该完成的操作。这这种种函函数数称称为为运运算算符符重重载载函数

46、函数,通常是类的成员函数或是友元函数,运算符的操作数通常也是该类的对象。定义一个运算符重载函数函数名必须以operator开头,一般形式如下::operator()/函数体重载的运算符必须是一个合法的运算符,如“+”、“-”、“*”、“/”、“+”等。例例Ex_Complex Ex_Complex 运算符的简单重载。#include class CComplexpublic:CComplex(double r=0,double i=0)realPart=r;imagePart=i;void print()cout该复数实部=realPart,虚部=imagePartendl;CComplex

47、operator+(CComplex&c);/重载运算符+CComplex operator+(double r);/重载运算符+private:double realPart;/复数的实部double imagePart;3.4运算符重载运算符重载CComplex CComplex:operator+(CComplex&c)/参数是CComplex引用对象CComplex temp;temp.realPart=realPart+c.realPart;temp.imagePart=imagePart+c.imagePart;return temp;CComplex CComplex:opera

48、tor+(double r)/参数是double类型数据CComplex temp;temp.realPart=realPart+r;temp.imagePart=imagePart;return temp;void main()CComplex c1(12,20),c2(50,70),c;c=c1+c2;c.print();c=c1+20;c.print();运行结果为:该复数实部=62,虚部=90该复数实部=32,虚部=203.4运算符重载运算符重载还需要说明的是:(1)(1)当用成员函数实现双目运算符的重载时,运运算算符符的的左左操操作作数数一一定定是是对对象象,右右操操作作数数作作为为

49、调调用用运运算算符符重重载载函函数数的的参参数数,参参数数可可以以是是对对象象、对对象象的的引引用用或或是是其其他他类类型型的的参参数数。例如,若有表达式“c=20+c1”,则编译器必将“20+c1”解释为“20.operator+(c1)”,显然出现编译错误。但实际应用时,这种运算操作是存在的,解决这个问题的办法是将运算符重载为友元函数(在后面讨论)。(2)(2)不不是是所所有有的的运运算算符符都都可可以以重重载载。在C+中不允许重载的运算符除三目运算符“?:”外,还有成员操作符“.”、成员指针操作符“*”、作用域操作符“:”以及sizeof运算符。(3)(3)只只能能对对C+C+中中已已定

50、定义义了了的的运运算算符符进进行行重重载载,而且当重载一个运算符时,该运算符的操作数个数、优先级和结合性是不能改变的。3.4运算符重载运算符重载3.4.2友元重载友元重载实现运算符重载的方法有两种:用用类类的的成成员员函函数数来来实实现现和和通通过过类类的的友友元元函函数数来实现来实现。这里来用友元函数实现重载的方法。友元重载方法既可用于单目运算符,也可以用于双目运算符。它们的一般格式如下:friend operator()/单目运算符重载 /函数体friend operator()/双目运算符重载 /函数体 对对于于单单目目运运算算符符的的友友元元重重载载函函数数来来说说,只只有有一一个个形

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

当前位置:首页 > 生活休闲 > 生活常识

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

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