第2章C++面向对象程序设计_第1页
第2章C++面向对象程序设计_第2页
第2章C++面向对象程序设计_第3页
第2章C++面向对象程序设计_第4页
第2章C++面向对象程序设计_第5页
已阅读5页,还剩94页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第第2章章C+面向对象程序设计面向对象程序设计在传统的结构化程序设计方法中,数据和处理数据的程序是分离的。当对某段程序进行修改或删除时,整个程序中所有与其相关的部分都要进行相应的修改,从而程序代码的维护比较困难。为了避免这种情况的发生,C+引用了面向对象的设计方法,它是将数据及处理数据的相应函数“封装”到一个“类”中,类的实例称为“对象”。在一个对象内,只有属于该对象的函数才可以存取该对象的数据。这样,其他函数就不会无意中破坏它的内容,从而达到保护和隐藏数据的效果。2.1.1 类的定义类的定义类的定义一般地分为声明部分和实现部分。声明部分是用来声明该类中的成员,包含数据成员(或称“成员变量”)

2、的声明和成员函数的声明。成员函数是用来对数据成员进行操作的,又称为“方法”。实现部分是用来对成员函数的定义。概括说来,声明部分将告诉使用者“干什么”,而实现部分是告诉使用者“怎么干”。C+中定义类的一般格式如下:class private:public:;其中,class是定义类的关键字,class的后面是用户定义的类名,通常用大写的C字母开始的标识符作为类名,C用来表示类(Class),以与对象、函数及其他数据类型相区别。类中的数据和函数是类的成员,分别称为数据成员和成员函数。 2.1.1 类的定义类的定义定义类时还应注意:(1) 类中的数据成员的数据类型可以是任意的,但不允许对所定义的数据

3、成员进行初始化,例如类CMeter中,下面的定义是错误的:class CMeter.private:int m_nPos = 10;/ 错误.;(2) 在“public:”或“private:”后面定义的所有成员都是公有或私有的,直到下一个“public:”或“private:”出现为止。若成员前面没有类似“public:”或“private:”,则所定义的成员是private(私有),这是类的默认设置。(3) 关键字public和private可以在类中出现多次,且前后的顺序没有关系;但最好先声明公有成员,后声明私有成员,因为public成员是用户最关心的。(4) 除了public和priv

4、ate外,关键字protected(保护)也可修饰成员的类型,它与private两者基本相似,但在类的继承时有所不同(后面还会讲到)。(5) 数据成员的类型可以是任意的,包含整型、浮点型、字符型、数组、指针和引用等,也可以是另一个类的对象。(6) 尽量将类单独存放在一个文件中或将类的声明放在.h文件中而将成员函数的实现放在与.h文件同名的.cpp文件中。以后将会看到,Visual C+ 6.0为用户创建的应用程序框架中都是将各个类以.h和同名的.cpp文件组织的。2.1.2 对象的定义对象的定义一个类定义后,就可以定义该类的对象,如下面的格式: 其中,类名是用户已定义过的类的标识符,对象名可以

5、有一个或多个,多个时要用逗号分隔。被定义的对象既可以是一个普通对象,也可以是一个数组对象或指针对象。例如:CMeter myMeter, *Meter, Meters2;这时,myMeter是类CMeter的一个普通对象,Meter和Meters分别是该类的一个指针对象和对象数组。一个对象的成员就是该对象的类所定义的数据成员(成员变量)和成员函数。访问对象的成员变量和成员函数和访问变量和函数的方法是一样的,只不过要在成员前面加上对象名和成员运算符“.”,其表示方式如下:.()2.1.3 构造函数和析构函数构造函数和析构函数1. 构造函数构造函数前面已提及,在类的定义中是不能对数据成员进行初始化

6、的。为了能给数据成员自动设置某些初始值,这时就要使用类的特殊成员函数构造函数。构造函数的最大特点是在对象建立时它会被自动执行,因此用于变量、对象的初始化代码一般放在构造函数中。C+规定:构造函数必须与相应的类同名,它可以带参数,也可以不带参数,与一般的成员函数定义相同,也可以重载。例如:class CMeterpublic:CMeter(int nPos )/ 带参数的构造函数 m_nPos = nPos;.这样若有:CMeter oMeter(10), oTick(20);则会自动调用构造函数CMeter(int nPos ),从而使得对象oMeter中的私有成员m_nPos的值为10;使得

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

8、造函数和析构函数3. 默认构造函数和析构函数默认构造函数和析构函数系统自动生成的默认构造函数和析构函数如下所示:CMeter( )/ 默认构造函数的形式 CMeter( )/ 默认析构函数的形式 需要说明的是,在用户定义一个对象时,编译器会自动根据对象定义的格式选择相应的构造函数。例如:CMeter m1, m2;由于m1和m2不带任何参数,当类没有用户定义的构造函数时,则编译器就会使用默认构造函数对m1和m2进行初始化。用默认构造函数对对象进行初始化时,则将对象的所有数据成员都初始化为零或空。2.1.3 构造函数和析构函数构造函数和析构函数4. 构造函数的重载构造函数的重载构造函数可以被重载

9、,C+会根据对象定义中的参数选择合适的构造函数。例如:例例Ex_ConOverLoad 构造函数的重载构造函数的重载#include class CDatepublic:CDate();CDate(int day);CDate(int month, int day);CDate(int year, int month, int day);/ 其他公共成员private:int nYear, nMonth, nDay;CDate:CDate()nMonth = 7; nDay = 30; nYear = 2002;coutnYear-nMonth-nDayendl;CDate:CDate(int

10、 day)nMonth = 7; nDay = day; nYear = 2002;coutnYear-nMonth-nDayendl;CDate:CDate(int month, int day)nMonth = month; nDay = day; nYear = 2002;coutnYear-nMonth-nDayendl;CDate:CDate(int year, int month, int day)nYear = year; nMonth = month; nDay = day; coutnYear-nMonth-nDayendl;void main()CDate day1;CDa

11、te day2(28);CDate day3(8, 1);CDate day4(2003, 3, 10);运行结果为:2.1.3 构造函数和析构函数构造函数和析构函数例例Ex_ConDefault 带默认参数的构造函数带默认参数的构造函数#include class CDate public: CDate(int year = 2002, int month = 7, int day = 30) nYear = year; nMonth = month; nDay = day; coutnYear-nMonth-nDayendl; / 其他公共成员 private: int nYear, nM

12、onth, nDay;void main()CDate day1;CDate day2(2002, 8);运行结果为:2.1.3 构造函数和析构函数构造函数和析构函数5. 拷贝构造函数拷贝构造函数拷贝构造函数是一种特殊的成员函数,它的功能是用一个已知的对象来初始化一个被创建的同类的对象。拷贝构造函数的函数名与构造函数一样,也是它所属类的类名。不过与一般的构造函数有一点不同,即它只有一个参数,且参数是同类的一个对象的引用。定义一个拷贝构造函数的一般形式如下::(const&)其中,const是一个类型修饰符,被它修饰的对象是一个不能被更新的常量。例如:2.1.3 构造函数和析构函数构造函

13、数和析构函数例例Ex_ConCopy 拷贝构造函数的使用拷贝构造函数的使用#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)/ 定义的拷贝构造函数cout调用拷贝构造函数endl; coutymd.nYear-ymd.nMonth-ymd.nDayendl;/ 其他公共成

14、员private:int nYear, nMonth, nDay;void main()CDate day1(2002, 8);CDate day2(day1);2.1.3 构造函数和析构函数构造函数和析构函数例例Ex_ConCopy 拷贝构造函数的使用拷贝构造函数的使用运行结果为:2.1.3 构造函数和析构函数构造函数和析构函数5. 拷贝构造函数拷贝构造函数实际上,如果类中没有声明拷贝构造函数,则编译器自动生成一个默认的拷贝构造函数。例如:例例Ex_ConCopyDefault 默认拷贝构造函数的使用默认拷贝构造函数的使用#include class CDatepublic:CDate(in

15、t year = 2002, int month = 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();2.1.3 构造函数和析构函数构造函数和析构函数例例Ex_ConCopyDefau

16、lt 默认拷贝构造函数的使用默认拷贝构造函数的使用运行结果为:2.1.4 对象成员初始化对象成员初始化前面所遇到的都是单独的一个类,但在实际应用中往往需要多个类,这时就可能把一个已定义类的对象作为另一个类的成员。为了能对这些对象成员进行初始化,C+允许采用这样的构造函数定义格式::(形参表):对象1(参数表), 对象2(参数表), , 对象n(参数表) 其中,对象1、对象2、对象n就是该类使用的其他类的对象,冒号“:”后面的列表称为成员初始化列表。下面来看一个示例:2.1.4 对象成员初始化对象成员初始化例例Ex_InitMultObject 对象成员的初始化对象成员的初始化#include

17、class CPoint public:CPoint(int x, int y) nPosX = x; nPosY = y;void ShowPos() cout当前位置:x = nPosX, y = nPosYendl;private:int nPosX, nPosY;class CSize public:CSize(int l, int w) nLength = l; nWidth = w; void ShowSize() cout当前大小:l = nLength, w = nWidthendl; private:int nLength, nWidth;class CRectpublic:

18、CRect(int left, int top, int right, int bottom):size(right-left, bottom-top),ptCenter(left+right)/2, (top+bottom)/2) void Show()ptCenter.ShowPos();size.ShowSize();private:CPoint ptCenter;CSize size;void main()CRect rc(10, 100, 80, 250);rc.Show();运行结果为:代码中,声明类CRect的构造函数时,将成员CPoint类对象ptCenter和CSize类对象

19、size按CRect构造函数的形参进行初始化。 2.1.5 静态成员静态成员静态成员的提出是为了解决数据共享的问题。1. 静态数据成员静态数据成员它是这样定义的:(1) 使用关键字static声明静态数据成员。(2) 对静态数据成员进行初始化。由于静态数据成员要实际在分配空间,因此不能在类声明中进行初始化。静态数据成员初始化在类的外部进行,且与一般数据成员初始化不同,它的格式如下::=例如:2.1.5 静态成员静态成员例例Ex_StaticData 静态数据成员的使用静态数据成员的使用#include class CSumpublic:CSum(int a = 0, int b = 0) nS

20、um += a+b; int GetSum() return nSum; void SetSum(int sum) nSum = sum;private:static int nSum;/ 声明静态数据成员;int CSum:nSum = 0;/ 静态数据成员的初始化void main()CSum one(10, 2), two;coutsum = one.GetSum()endl;one.SetSum(5);coutsum = one.GetSum()endl;coutsum = two.GetSum()endl;运行结果为:2.1.5 静态成员静态成员2. 静态成员函数静态成员函数例例Ex

21、_StaticFunc 静态成员函数的使用静态成员函数的使用#include class CSumpublic:CSum(int a = 0, int b = 0) nSum += a+b; int GetSum() return nSum; void SetSum(int sum) nSum = sum; static void ShowData(CSum one); / 声明静态成员函数private:static int nSum;void CSum:ShowData(CSum one) / 静态成员函数的实现cout直接使用静态成员endl;coutsum = nSumendl;cou

22、t使用同类的对象endl;coutsum = one.GetSum()endl;int CSum:nSum = 0;void main()CSum one(10, 2);CSum:ShowData(one);/ 直接访问one.SetSum(8);one.ShowData(one);/ 通过对象访问运行结果为:2.1.6 友元友元例例Ex_FriendFunc 友元函数的使用友元函数的使用#include class CPoint public: CPoint() m_x = m_y = 0; CPoint( unsigned x, unsigned y ) m_x = x; m_y = y;

23、 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_y ptTemp.m_y += nOffset; return ptTemp; void mai

24、n() CPoint pt( 10, 20 ); pt.Print(); pt = Inflate(pt, 3); / 调用友元函数 pt.Print();运行结果为:2.1.7 常类型常类型常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。因此,定义或说明常类型时必须进行初始化。1. 常对象常对象常对象是指对象常量,定义格式如下: const 定义常对象时,修饰符const可以放在类名后面,也可以放在类名前面。例如:class COne public: COne(int a, int b) x = a; y = b; private: int x, y;co

25、nst COne a(3,4);COne const b(5,6);其中,a和b都是COne对象常量,初始化后就不能再被更新。2.1.7 常类型常类型2. 常指针和常引用常指针和常引用常指针也是使用关键字const来修饰的。但需要说明的是,const的位置不同,其含意也不同,它有三种形式。第一种形式是将const放在指针变量的类型之前,表示声明一个指向常量的指针。此时,在程序中不能通过指针来改变它所指向的数据值,但可以改变指针本身的值。例如:int a = 1, b = 2;const int *p1 = &a; / 声明指向int型常的指针p1, 指针地址为a的地址*p1 = 2;/

26、 错误,不能更改指针所指向的数据值p1 = &b;/ 正确,指向常量的指针本身的值是可以改变的第二种形式是将const放在指针定义语句的指针名前,表示指针本身是一个常量,称为指针常量或常指针。因此,不能改变这种指针变量的值,但可以改变指变量所指向的数据值。例如:int a = 1, b = 2;int * const p1 = &a; / 声明指向int型常的指针p1, 指针地址为a的地址int * const p2;/ 错误,在声明指针常量时,必须初始化*p1 = 2;/ 正确,指针所指向的数据值可以改变p1 = &b;/ 错误,指针常量本身的值是不可改变的第三种形式

27、是将const在上述两个地方都加,表示声明一个指向常量的指针常量,指针本身的值不可改变,而且它所指向的数据的值也不能通过指针改变。例如:int a = 1, b = 2;const int * const pp = &a;*pp = 2;/ 错误pp = &b;/ 错误2.1.7 常类型常类型例Ex_ConstPara 常参数的函数传递#include class COnepublic:void print(const int *p, int n)/ 使用常参数 cout*p;for (int i = 1; in; i+)cout, *(p+i);coutendl;void m

28、ain()int array6 = 1, 2, 3, 4, 5, 6;COne one;one.print(array, 6);运行结果为:2.1.7 常类型常类型3. 常成员函数常成员函数使用const关键字进行声明的成员函数,称为“常成员函数”。只有常成员函数才有资格操作常量或常对象,没有使用const关键字说明的成员函数不能用来操作常对象。常成员函数说明格式如下: () const;其中,const是加在函数说明后面的类型修饰符,它是函数类型的一个组成部分,因此,在函数实现部分也要带const关键字。例如:2.1.7 常类型常类型例例Ex_ConstFunc 常成员函数的使用常成员函数的

29、使用#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() const cout使用常成员函数:x, yendl;void main() COne one(5, 4); one.print(); const COne two(20, 52); two.print();运行结果为:程序中,类COne声明了两个重载成员函

30、数,一个带const,一个不带。语句“one.print();”调用成员函数“void print();”,而“two.print();”调用常成员函数“void print() const;”。2.1.7 常类型常类型4. 常数据成员常数据成员类型修饰符const不仅可以说明成员函数,也可以说明数据成员。由于const类型对象必须被初始化,并且不能更新,此,在类中声明了const数据成员时,只能通过成员初始化列表的方式来生成构造函数对数据成员初始化。例如:例Ex_ConstData 常数据成员的使用#include class COne public: COne(int a):x(a),r(

31、x)/ 常数据成员的初始化 void print(); const int &r;/ 引用类型的常数据成员 private: const int x;/ 常数据成员 static const int y;/ 静态常数据成员;const int COne:y = 10;/ 静态数据成员的初始化void COne:print() coutx = x, y = y, r = rendl;void main() COne one(100); one.print();该程序的运行结果为:2.1.8 this指针指针this指针是一个仅能被类的非静态成员函数所能访问的特殊指针。当一个对象调用成员函

32、数时,编译器先将对象的地址赋给this指针,然后调用成员函数。例如,当下列成员函数调用时:one.copy(two);它实际上被解释成:copy( &one, two);只不过,&one参数为隐藏了。需要说明的是,通过*this可以判断是哪个对象来调用该成员函数或重新指定对象。例如:2.1.8 this指针指针例例Ex_This this指针的使用指针的使用#include class COnepublic: COne() x = y = 0; COne(int a, int b) x = a; y = b; void copy(COne &a); / 对象引用作函数参

33、数 void print() coutx , yM这样的表达式中,其中pa是一个指向A类对象的指针。2.1.9 类的作用域和对象的生存期类的作用域和对象的生存期对象的生存期是指对象从被创建开始到被释放为止的时间。按生存期的不同,对象可分为如下三种:(1) 局部对象:当对象被定义时调用构造函数,该对象被创建,当程序退出定义该对象所在的函数体或程序块时,调用析构函数,释放该对象。(2)静态对象:当程序第一次执行所定义的静态对象时,该对象被创建,当程序结束时,该对象被释放。(3) 全局对象:当程序开始时,调用构造函数创建该对象,当程序结束时调用析构函数释放该对象。2.2 继承和派生类继承和派生类2.

34、2.1. 单继承单继承从一个基类定义一个派生类可按下列格式:class : ;其中,继承方式有3种:public(公有)、private(私有)及protected(保护),若继承方式没有指定,则被指定为默认的public方式。继承方式决定了派生类的继承基类属性的使用权限,下面分别说明。2.2.1. 单继承单继承1. 公有继承公有继承(public)公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的。例如:class CStick : public CMeterint m_nStickNum;/ 声明一个私有数据成员 public:v

35、oid DispStick();/ 声明一个公有成员函数;/ 注意分号不能省略void CStick: DispStick() m_nStickNum = GetPos();/ 调用基类CMeter的成员函数coutm_nStickNum ;这时,从基类CMeter派生的CStick类除具有CMeter所有公有成员和保护成员外,还有自身的私有数据成员m_nStickNum和公有成员函数DispStick()。这个完整的示例如下: 2.2.1. 单继承单继承例例Ex_ClassPublicDerived 派生类的公有继承示例派生类的公有继承示例#include class CMeterpubli

36、c:CMeter(int nPos = 10) m_nPos = nPos; CMeter() void StepIt() m_nPos+; int GetPos()return m_nPos; protected:void SetPos(int nPos) m_nPos = nPos; private:int m_nPos;class CStick : public CMeter/ 从CMeter派生,公有继承intm_nStickNum;/ 声明一个私有数据成员public:void DispStick();/ 声明一个公有成员函数void SetStick(int nPos)SetPos

37、(nPos);/ 类中调用基类的保护成员;void CStick: DispStick()m_nStickNum = GetPos();/ 调用基类CMeter的成员函数coutm_nStickNum ;void main()CMeter oMeter(20);CStick oStick;coutCMeter:oMeter.GetPos(),CStick:oStick.GetPos()endl; oMeter.StepIt(); coutCMeter:oMeter.GetPos(),CStick:oStick.GetPos()endl; oStick.StepIt(); outCMeter:o

38、Meter.GetPos(),CStick:oStick.GetPos()endl; oStick.DispStick(); oStick.StepIt(); oStick.DispStick();运行结果为:2.2.1. 单继承单继承2. 私有继承私有继承(private)私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。例如:例例Ex_ClassPrivateDerived 派生类的私有继承示例派生类的私有继承示例#include class CMeterpublic:CMeter(int nPos = 10) m_nPos = nPos;

39、CMeter() void StepIt() m_nPos+; int GetPos()return m_nPos; protected:void SetPos(int nPos) m_nPos = nPos; private:int m_nPos;class CStick : private CMeter/ 从CMeter派生,私有继承int m_nStickNum;/ 声明一个私有数据成员public:void DispStick();/ 声明一个公有成员函数void SetStick(int nPos) SetPos(nPos);/ 调用基类的保护成员 int GetStick() re

40、turn GetPos();/ 调用基类的公有成员 ;void CStick: DispStick()m_nStickNum = 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();运行

41、结果为:2.2.1. 单继承单继承3. 保护继承保护继承(protected)保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。表2.1列出三种不同的继承方式的基类特性和派生类特性。表表2.1 不同继承方式的基类特性和派生类特性不同继承方式的基类特性和派生类特性2.2.2 派生类的构造函数和析构函数派生类的构造函数和析构函数例例Ex_ClassDerived 派生类的构造函数和析构函数的示例派生类的构造函数和析构函数的示例#include #include class CAnimalpublic:CAnima

42、l(char *pName = noname);CAnimal();void setName(char *pName)strncpy(name, pName, sizeof(name);char *getName(void) return name; private:char name20;CAnimal:CAnimal(char *pName)setName(pName);cout调用CAnimal的构造函数!endl;CAnimal:CAnimal()cout调用CAnimal的析构函数!endl;class CCat : public CAnimalpublic:CCat()cout调用

43、CCat的构造函数!endl;CCat()cout调用CCat的析造函数!endl;void DispName()cout猫的名字是:getName()endl;void main()CCat cat;cat.DispName();cat.setName(Snoopy);cat.DispName();运行结果为:2.2.2 派生类的构造函数和析构函数派生类的构造函数和析构函数例如,在Ex_ClassPublicDerived示例中,CStick的构造函数可这样定义:class CStick : public CMeter. public: CStick():CMeter(30) .;此时再重新

44、运行程序,结果就会变为:2.2.3 多继承多继承多继承下派生类的定义是按下面的格式:class : , ,.;其中的继承方式还是前面的3种:public、private和protected。例如:class A.class B.class C:public A,private B.由于派生类C继承了基类A和B,具有多继承性,因此派生类C的成员包含了基类A中成员和B中成员以及该类本身的成员。 2.2.4虚基类虚基类一般说来,在派生类中对基类成员的访问应该是惟一的,但是,由于多继承情况下,可能造成对基类中某成员的访问出现了不惟一的情况,这种情况称为基类成员调用的二义性。例如:例例Ex_Confli

45、ct 基类成员调用的二义性基类成员调用的二义性#include class A public: int x; A(int a = 0) x = a; ;class B1 : public A public: int y1; B1( int a = 0, int b = 0):A(b) y1 = a;class B2 : public A public: int y2; B2( int a = 0, int b = 0):A(b) y2 = a;class C : public B1, public B2 public: int z; C(int a, int b, int d, int e,

46、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();2.2.4虚基类虚基类程序中,派生类B1和B2都从基类A继承,这时在派生类中就有两个基类A的拷贝。当编译器编译到“cout”x = “xendl;”语句时,因无法确定成员x是从类B1中继承来的,还是从类B2继承来,产生了二义性,从而出现编译错误。解决这个问题的方法之一是使用域作用运算符“

47、 :”来消除二义性,例如若将print()函数实现代码变为:void print()coutB1:x = B1:xendl;coutB2:x = B2:xendl;couty1 = y1, y2 = y2endl;coutz = zendl;重新运行,结果为:2.3.1 虚函数虚函数先来看一个虚函数应用实例。例例Ex_VirtualFunc 虚函数的使用虚函数的使用#include class CShapepublic:virtual float area()/ 将area定义成虚函数 return 0.0;class CTriangle:public CShapepublic:CTriang

48、le(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;void main()CShape *s2;s0 = new CTriangle(3,4);coutarea()endl;s1 = new CCircle(5);coutarea()area

49、();”实际上是调用CTriangle类的area成员函数,结果是6;同样可以分析s1-area()的结果。需要说明的是:(1) 虚函数在重新定义时参数的个数和类型必须和基类中的虚函数完全匹配,这一点和函数重载完全不同。(2) 只有通过基类指针才能实现虚函数的多态性,若虚函数的调用是普通方式来进行的,则不能实现其多态性。(3) 如果不使用new来创建相应的派生类对象指针,也可使用通过&运算符来获取对象的地址。(4) 虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态的成员函数。(5) 可把析构函数定义为虚函数,但不能将构造函数定义为虚函数。 声明纯虚函数的一般格式为:virtu

50、al () = 0;2.3.2 纯虚函数和抽象类纯虚函数和抽象类例例Ex_PureVirtualFunc 纯虚函数和抽象类的使用纯虚函数和抽象类的使用#include class CShapepublic:virtual float area() = 0;/ 将area定义成纯虚函数;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;cla

51、ss CCircle:public CShapepublic:CCircle(float r) R = r;float area() / 在派生类定义纯虚函数的具体实现代码return (float)(3.14159265 * R * R);private:float R;void main()CShape *pShape;CTriangle tri(3, 4);couttri.area()endl;pShape = &tri;coutarea()endl;CCircle cir(5);coutcir.area()endl;pShape = ○coutarea()end

52、l;运行结果为:2.4.1 运算符重载的语法运算符重载的语法定义一个运算符重载函数与定义普通函数相类似,只不过函数名必须以operator开头,其一般形式如下::operator () / 函数体 由于运算符重载函数的函数是以特殊的关键字开始的,编译器很容易与其他的函数名区分开来。先来看一个实例,这个例子是定义一个复数类CComplex,然后重载“+”运算符,使这个运算符能直接完成复数的加运算。2.4.1 运算符重载的语法运算符重载的语法例例Ex_Complex 运算符的简单重载运算符的简单重载#include class CComplexpublic:CComplex(double r =

53、0, double i = 0) realPart = r;imagePart = i;void print()cout该复数实部 = realPart, 虚部 = imagePartendl;CComplex operator + (CComplex &c);/ 重载运算符+CComplex operator + (double r);/ 重载运算符+private:double realPart;/ 复数的实部double imagePart;/ 复数的虚部;CComplex CComplex:operator + (CComplex &c) / 参数是CComplex引用

54、对象CComplex temp;temp.realPart = realPart + c.realPart;temp.imagePart = imagePart + c.imagePart;return temp;CComplex CComplex:operator + (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

55、.print();c = c1+ 20;c.print();运行结果为:2.4.2 友元重载友元重载实现运算符重载的方法有两种:用类的成员函数来实现和通过类的友元函数来实现。这里来用友元函数实现重载的方法。友元重载方法既可用于单目运算符,也可以用于双目运算符。它们的一般格式如下:friend operator ()/ 单目运算符重载 / 函数体friend operator ()/ 双目运算符重载 / 函数体 重载函数来说,它有两个形参,这两个形参中必须有一个是类的对象。 需要说明的是,有些运算符是不能重载为友元函数的,它们是:=、()、和-。2.4.2 友元重载友元重载例例Ex_Comple

56、xFriend 运算符的友元重载运算符的友元重载#include class CComplexpublic:CComplex(double r = 0, double i = 0) realPart = r;imagePart = i;void print()cout该复数实部 = realPart, 虚部 = imagePartendl;CComplex operator + (CComplex &c);/ 重载运算符+CComplex operator + (double r);/ 重载运算符+friend CComplex operator + (double r, CCompl

57、ex &c);/ 友元重载运算符+friend CComplex operator - (CComplex &c);/ 友元重载单目运算符-void operator += (CComplex &c);private:double realPart;/ 复数的实部double imagePart;/ 复数的虚部;CComplex CComplex:operator + (CComplex &c) CComplex temp;temp.realPart = realPart + c.realPart;temp.imagePart = imagePart + c.i

58、magePart;return temp;CComplex CComplex:operator + (double r) CComplex temp;temp.realPart = realPart + r;temp.imagePart = imagePart;return temp;CComplex operator + (double r, CComplex &c)CComplex temp;temp.realPart = r + c.realPart;temp.imagePart = c.imagePart;return temp;CComplex operator - (CCo

59、mplex &c)return CComplex(-c.realPart, -c.imagePart);void CComplex:operator += (CComplex &c)realPart += c.realPart;imagePart += c.imagePart;void main()CComplex c1(12,20), c2(30,70), c;c = c1 + c2;c.print();c = c1 + 20;c.print();c = 20 + c2;c.print();c2 += c1;c2.print();c1 = -c1;c1.print();运行结

60、果为:2.4.3 转换函数转换函数类型转换是将一种类型的值映射为另一种类型的值。C+的类型转换包含自动隐含和强制转换的两种方法。转换函数是实现强制转换的手段之一,它是类中定义的一个非静态成员函数,其一般格式为;class public: operator (); 其中,是要转换后的一种数据类型,它可以是基本的数据类型,也可以是导出的的数据类型。operator和一起构成了转换函数名。这个转换函数的作用是将“class”声明的类对象转换成指定的数据类型。下面举一例来说明,这个例子是将金额的小写形式(数字)转换成金额的大写形式(汉字)。2.4.3 转换函数转换函数例例Ex_Money 转换函数的使用转换函数的使用#include #include typedef char* string;class CMoneypublic:CMoney(double a = 0.0) amount = a; operato

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论