1、C+面向对象程序设计2.1 2.1 类和对象类和对象2.1.1 2.1.1 类的定义类的定义C+中定义类的一般格式如下:class private:public:protected:;2.1.1 2.1.1 类的定义类的定义当类的成员函数的函数体在类的外部定义时,必须由作用域运算符“:”来通知编译系统该函数所属的类。例如:class CMeterpublic:double m_nPercent;/声明一个公有数据成员void StepIt();/声明一个公有成员函数void SetPos(int nPos);/声明一个公有成员函数int GetPos()return m_nPos;/声明一个公
2、有成员函数并定义private:int m_nPos;/声明一个私有数据成员;/注意分号不能省略void CMeter:StepIt()m_nPos+;void CMeter:SetPos(int nPos)m_nPos=nPos;2.1.2 2.1.2 对象的定义对象的定义与结构类型一样,它也有三种定义方式:声明之后定义、声明之时定义和一次性定义。但由于“类”比任何数据类型都要复杂得多,为了提高程序的可读性,真正将“类”当成一个密闭、“封装”的盒子(接口),在程序中应尽量在对象的声明之后定义方式,并按下列格式进行:一个对象的成员就是该对象的类所定义的数据成员(成员变量)和成员函数。访问对象的
3、成员变量和成员函数和访问变量和函数的方法是一样的,只不过要在成员前面加上对象名和成员运算符“.”,其表示方式如下:.()例如:myMeter.m_nPercent,myMeter.SetPos(2),Meters0.StepIt();2.1.2 2.1.2 对象的定义对象的定义若对象是一个指针,则对象的成员访问形式如下:-()“-”是一个表示成员的运算符,它与“.”运算符的区别是:“-”用来表示指向对象的指针的成员,而“.”用来表示一般对象的成员。2.1.3 2.1.3 类作用域和成员访问权限类作用域和成员访问权限1 1类名的作用域类名的作用域如果在类声明之前就需要使用该类名定义对象,则必须用
4、下列格式在使用前进行提前声明(注意,类的这种形式的声明可以在相同作用域中出现多次):class;例如:class COne;class COne;/将类COne提前声明class COne;class COne;/可以声明多次class CTwo/private:COne a;/数据成员a是已定义的COne类对象;class COne/;2.1.3 2.1.3 类作用域和成员访问权限类作用域和成员访问权限2 2类中成员的可见性类中成员的可见性(1)在类中使用成员时,成员声明的前后不会影响该成员在类中的使用,这是类作用域的特殊性。例如:class Avoid f1()f2()f2();/调用类中
5、的成员函数f2coutaendl;/使用类中的成员变量avoid f2()int a;2.1.3 2.1.3 类作用域和成员访问权限类作用域和成员访问权限(2)由于类的成员函数可以在类体外定义,因而此时由“类名:”指定开始一直到函数体最后一个花括号为止的范围也是该类作用域的范围。例如:class Avoid f1();/;void A A:f1()/则从A:开始一直到f1函数体最后一个花括号为止的范围都是属于类A的作用域。(3)在同一个类的作用域中,不管成员具有怎样的访问权限,都可在类作用域中使用,而在类作用域外却不可使用。例如:class Apublic:int a;/;a=10;/错误,不
6、能在A作用域外直接使用类中的成员2.1.3 2.1.3 类作用域和成员访问权限类作用域和成员访问权限3 3类外对象成员的可见性类外对象成员的可见性对于访问权限public、private和protected来说,只有在子类中或用对象来访问成员时,它们才会起作用。在用类外对象来访问成员时,只能访问public成员,而对private和protected均不能访问。2.1.4 2.1.4 构造函数和析构函数构造函数和析构函数1 1构造函数构造函数C+规定,一个类的构造函数必须与相应的类同名,它可以带参数,也可以不带参数,与一般的成员函数定义相同,可以重载,也可以有默认的形参值。例如:class C
7、Meterpublic:CMeter(int nPos)/带参数的构造函数m_nPos=nPos;/这样若有:CMeter oMeter(10),oTick(20);2.1.4 2.1.4 构造函数和析构函数构造函数和析构函数2 2对构造函数的几点说明对构造函数的几点说明(1)构造函数的约定使系统在生成类的对象时自动调用。同时,指定对象括号里的参数就是构造函数的实参,例如,oMeter(10)就是oMeter.CMeter(10)。故当构造函数重载及设定构造函数默认形参值时,要避免出现二义。CPerson(char*str,float h=170,float w=130)/Astrcpy(na
8、me,str);height=h;weight=w;CPerson(char*str)/Bstrcpy(name,str);2.1.4 2.1.4 构造函数和析构函数构造函数和析构函数(2)定义的构造函数不能指定其返回值的类型,也不能指定为void 类型。事实上,由于构造函数主要用于对象数据成员的初始化,因而无须返回函数值,也就无须有返回类型。(3)若要用类定义对象,则构造函数必须是公有型成员函数,否则类无法实例化(即无法定义对象)。若类仅用于派生其他类,则构造函数可定义为保护型成员函数。2.1.4 2.1.4 构造函数和析构函数构造函数和析构函数3 3默认构造函数默认构造函数实际上,在类定义
9、时,如果没有定义任何构造函数,则编译自动为类隐式生成一个不带任何参数的默认构造函数,由于函数体是空块,因此默认构造函数不进行任何操作,仅仅为了满足对象创建时的语法需要。其形式如下:()例如,对于CMeter类来说,默认构造函数的形式如下:CMeter()/默认构造函数的形式 默认构造函数的目的是使下列对象定义形式合法:CMeter one;/one.CMeter();会自动调用默认构造函数2.1.4 2.1.4 构造函数和析构函数构造函数和析构函数4 4析构函数析构函数与构造函数相对应的是析构函数。析构函数析构函数是另一个特殊的C+成员函数,它只是在类名称前面加上一个“”符号(逻辑非),以示与
10、构造函数功能相反。每一个类只有一个析构函数,没有任何参数,也不返回任何值。例如:class CMeterpublic:/CMeter()CMeter()/析构函数析构函数 /析构函数只有在下列两种情况下才会被自动调用:(1)当对象定义在一个函数体中,该函数调用结束后,析构函数被自动调用。(2)用new为对象分配动态内存,当使用delete释放对象时,析构函数被自动调用。2.1.4 2.1.4 构造函数和析构函数构造函数和析构函数5 5应用示例应用示例类的构造函数和析构函数的一个典型应用是在构造函数中用new为指针成员开辟独立的动态内存空间,而在析构函数中用delete释放它们。【例Ex_Nam
11、e】使用构造函数和析构函数由于“CName one(p);”调用的是B重载构造函数,从而使得私有指针成员strName的指向等于p的指向。而p指向new开辟的内存空间,其内容为“DING”,一旦p指向的内存空间删除后,p的指向就变得不确定了,此时strName指向也不确定,所以此时运行结果为:葺葺葺葺?2.1.4 2.1.4 构造函数和析构函数构造函数和析构函数显然,输出的是一个无效的字符串。因此,为了保证类的封装性,类中的指针成员所指向的内存空间必须在类中自行独立开辟和释放。因此,类CName应改成下列代码。这样,主函数中的代码才会有正确的运行结果:DING2.1.5 2.1.5 对象赋值和
12、拷贝对象赋值和拷贝1 1赋值赋值在C+中,一个类的对象的初值设定可以有多种形式。例如,对于前面的类CName来说,则可有下列对象的定义方式:CName o1;/通过A显式默认构造函数设定初值CName o2(DING);/通过B重载构造函数设定初值等都是合法有效的。但是若有:o1=o2;/通过赋值语句设定初值C+还常用下列形式的初始化来将另一个对象作为对象的初值:()例如:CName o2(DING);/A:通过构造函数设定初值CName o3(o2);/B:通过指定对象设定初值2.1.5 2.1.5 对象赋值和拷贝对象赋值和拷贝每一个类总有一个默认拷贝构造函数,其目的是保证B语句中对象初始化
13、形式的合法性,其功能就等价于“CName o3=o2;”。但语句“CName o3(o2);”与语句“o1=o2;”一样,也会出现程序终止的情况,其原因和“o1=o2;”的原因一样。但是,若有类CData:class CDatapublic:CData(int data=0)m_nData=data;CData()int getData()return m_nData;private:intm_nData;2.1.5 2.1.5 对象赋值和拷贝对象赋值和拷贝则下列初始化形式却都是合法有效的:CData a(3);/通过重载构造函数设定初值CData b(a);/通过默认拷贝构造函数设定初值/等
14、价于 CData b=a;cout a.getData()endl;/输出 3cout b.getData()endl;/输出 32.1.5 2.1.5 对象赋值和拷贝对象赋值和拷贝2 2浅拷贝和深拷贝浅拷贝和深拷贝每一个C+类都有一个隐式的默认拷贝构造函数,其目的是保证对象初始化方式的合法性,其功能是将一个已定义的对象所在的内存空间的内容依次拷贝到被初始化对象的内存空间中。这种仅仅将内存空间的内容拷贝的方式称为浅拷贝浅拷贝。3 3深拷贝构造函数深拷贝构造函数拷贝构造函数是一种比较特殊的构造函数,除遵循构造函数的声明和实现规则外,还应按下列格式进行定义。(参数表)对于CName的拷贝构造函数,
15、可有下列合法的函数原型:CName(CName&x);/x为合法的对象标识符CName(const CName&x);CName(CName&x,);/“”表示还有其他参数CName(const CName&x,);2.1.5 2.1.5 对象赋值和拷贝对象赋值和拷贝【例Ex_CopyCon】使用拷贝构造函数程序运行结果如下:DINGDING YOU HE2.1.6 2.1.6 对象成员的初始化对象成员的初始化为提高对象初始化效率,增强程序的可读性,C+允许在构造函数的函数头后面跟一个由冒号“:”来引导的对象成员初始化列表,列表中包含类中对象成员或数据成员的拷贝初始化代码,各对象初始化之间用逗
16、号分隔,如下列格式::(形参表):对象1(参数表),对象2(参数表),对象n(参数表)对象成员初始化列表对象成员初始化列表2.1.6 2.1.6 对象成员的初始化对象成员的初始化先来看看第一种方式(函数构造方式),例如。但若使用第二种方式(对象成员列表方式),即使用由冒号“:”来引导的对象成员初始化列表的形式,如下面的代码:class CPoint/;class CRectpublic:CRect(int x1,int y1,int x2,int y2):m_ptLT(x1,y1),m_ptRB(x2,y2):m_ptLT(x1,y1),m_ptRB(x2,y2)private:CPoint
17、m_ptLT,m_ptRB;int main()CRect rc(10,100,80,250);return 0;2.2 2.2 数据共享和成员特性数据共享和成员特性1 1静态数据成员静态数据成员与静态变量相似,静态数据成员是静态存储(static)的,但定义一个静态数据成员与一般静态变量不一样,它必须按下列两步进行:(1)在类中使用关键字static声明静态数据成员。在类中声明静态数据成员,仅仅是说明了静态数据成员是类中的成员这个关系,即便用该类定义对象时,该静态数据成员也不会分配内存空间。(2)在类外为静态数据成员分配内存空间并初始化。类中数据成员的内存空间是在对象定义时来分配的,但静态数
18、据成员的内存空间为所有该类对象所共享,只能分配一次,因而不能通过定义类对象的方式来分配,必须在类的外部作实际定义才能为所有对象共享,其定义格式如下::=2.2 2.2 数据共享和成员特性数据共享和成员特性【例Ex_StaticData】静态数据成员的使用#include class CSumpublic:CSum(int a=0,int b=0)/A nSum+=a+b;int getSum()return nSum;void setSum(int sum)nSum=sum;private:static int nSum;/声明静态数据成员;int CSum:nSum=0;/静态数据成员的实际
19、定义和初始化int main()CSum one(10,2),two;coutone:sum=one.getSum()endl;couttwo:sum=two.getSum()endl;two.setSum(5);coutone:sum=one.getSum()endl;couttwo:sum=two.getSum()endl;return 0;2.2 2.2 数据共享和成员特性数据共享和成员特性程序运行结果如下:one:sum=12two:sum=12one:sum=5two:sum=52.2 2.2 数据共享和成员特性数据共享和成员特性2 2静态成员函数静态成员函数在类中,静态数据成员可以
20、被成员函数引用,也可以被静态成员函数所引用。但反过来,静态成员函数却不能直接引用类中说明的非静态成员。假如静态成员函数可以引用类中的非静态成员,例如:class CSumpublic:static void ChangeData(int data)static void ChangeData(int data)nSum=data;nSum=data;/错误错误:引用类中的非静态成员 public:int nSum;则当执行语句:CSum:ChangeData(5);/合法的静态成员引用2.2 2.2 数据共享和成员特性数据共享和成员特性【例Ex_StaticMember】静态成员的使用程序运行
21、结果如下:20,40,-50,7,13,-50,7,13,20,40,2.2.2 2.2.2 友元友元1 1友元概述友元概述类的私有型(private)数据成员和保护型(protected)数据成员只能在类中由该类的成员函数来访问,类的对象及外部函数只能访问类的公有型(public)成员函数,类的私有和保护型数据成员只能通过类的成员函数来访问,如图2.1(a)所示。但是,如果在类中用friend关键字声明一个函数,且该函数的形参中还有该类的对象形参,这个函数便可通过形参对象或通过在函数体中定义该类对象来访问该类的任何私有和保护型数据成员,如图2.1(b)所示。2.2.2 2.2.2 友元友元2
22、 2友元函数友元函数友元函数可分为友元外部函数和友元成员函数。当一个函数f是A类的友元,且f还是另一个类B的成员函数时,则这样的友元称为友元成员函数;若f不属于任何类的成员,则这样的友元称为友元外部函数。通常将友元外部函数直接简称为友元友元函数函数。友元函数在类中定义的格式如下:friend (形参表)2.2.2 2.2.2 友元友元3 3友元成员函数友元成员函数友元成员函数在类中定义的格式如下:friend :(形参表)【例Ex_FriendMemFun】使用友元成员函数程序运行结果如下:Point(10,20)Rect(0,0,100,80)Point(13,23)Rect(3,3,103
23、,83)2.2.2 2.2.2 友元友元4 4友元类友元类除一个类的成员函数可以声明成另一个类的友元外,也可以将一个类声明成另一个类的友元,称为友元类友元类。当一个类作为另一个类的友元时,就意味着这个类的所有成员函数都是另一个类的友元成员函数。友元类的声明比较简单,其格式如下:friend class;【例Ex_ClassFriend】使用友元类程序运行结果如下:Point(22,28)2.2.3 2.2.3 常类型常类型1 1常对象常对象常对象是指对象常量,定义格式如下:const 定义常对象时,修饰符const可以放在类名后面,也可以放在类名前面。例如:class COnepublic:C
24、One(int a,int b)x=a;y=b;/private:int x,y;const COne a(3,4);COne const b(5,6);2.2.3 2.2.3 常类型常类型2 2常指针和常引用常指针和常引用const的位置不同,其含义也不同,它有3种形式。第1种形式是将const放在指针变量的类型之前,表示声明一个指向常量的指针。此时,在程序中不能通过指针来改变它所指向的数据值,但可以改变指针本身的值。例如:int a=1,b=2;const int*p1=&a;/声明指向int型常量的指针p1,指针地址为a的地址*p1=2;/错误,不能更改指针所指向的数据值p1=&b;/正
25、确,指向常量的指针本身的值是可以改变的2.2.3 2.2.3 常类型常类型第2种形式是将const放在指针定义语句的指针名前,表示指针本身是一个常量,称为指针常量或常指针常指针。例如:int a=1,b=2;int*const p1=&a;/声明指向int型常量的指针p1,指针地址为a的地址int*const p2;/错误,在声明指针常量时,必须初始化*p1=2;/正确,指针所指向的数据值可以改变p1=&b;/错误,指针常量本身的值是不可改变的第3种形式是将const在上述两个地方都加,表示声明一个指向常量的指针常量,指针本身的值不可改变,而且它所指向的数据的值也不能通过指针改变。例如:int
26、 a=1,b=2;const int*const pp=&a;*pp=2;/错误pp=&b;/错误2.2.3 2.2.3 常类型常类型【例Ex_ConstPara】有常参数的函数传递#include class COnepublic:void print(const int*p,int n)/使用常参数 cout*p;for(int i=1;in;i+)cout,*(p+i);coutendl;int main()int array6=1,2,3,4,5,6;COne one;one.print(array,6);return 0;程序运行结果如下:1,2,3,4,5,62.2.3 2.2.3
27、 常类型常类型3 3常成员函数常成员函数使用const关键字进行声明的成员函数,称为常成员函数常成员函数。只有常成员函数才有资格操作常量或常对象,没有使用const关键字说明的成员函数不能用来操作常对象。常成员函数说明格式如下:()const;【例Ex_ConstFunc】常成员函数的使用程序运行结果如下:5,4使用常成员函数:20,522.2.3 2.2.3 常类型常类型4 4常数据成员常数据成员类型修饰符const不仅可以说明成员函数,也可以说明数据成员。【例Ex_ConstData】常数据成员的使用程序运行结果如下:x=100,y=10,r=1002.2.4 this2.2.4 this
28、指针指针this指针是一个仅能被类的非静态成员函数所访问的特殊指针。当一个对象调用成员函数时,编译器先将对象的地址赋给this指针,然后调用成员函数。例如,当下列成员函数调用时:one.copy(two);它实际上被解释成:copy(&one,two);【例Ex_This】this指针的使用程序运行结果如下:0,03,42.2.4 this2.2.4 this指针指针事实上,若成员函数的形参名与该类的成员变量名同名,则必须用this指针来显式区分,例如:class CPointpublic:CPoint(int x=0,int y=0)this-x=x;this-x=x;this-y=y;th
29、is-y=y;void Offset(int x,int y)(*this).x+=x;this).x+=x;(*this).y+=y;this).y+=y;void Print()constcoutPoint(x,y)endl;private:int x,y;2.3 2.3 继承和派生继承和派生2.3.1 2.3.1 单继承单继承从一个基类定义一个派生类可按下列格式:class :;1 1公有继承(公有继承(publicpublic)公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的。例如:class CStick:public C
30、Meterpublic CMeterintm_nStickNum;/声明一个私有数据成员public:void DispStick();/声明一个公有成员函数;/注意分号不能省略void CStick:DispStick()m_nStickNum=GetPos();/调用基类CMeter的成员函数coutm_nStickNum;2.3.1 2.3.1 单继承单继承【例Ex_PublicDerived】派生类的公有继承示例程序运行结果如下:CMeter:20,CStick:10CMeter:21,CStick:10CMeter:21,CStick:1111 122.3.1 2.3.1 单继承单继
31、承2 2私有继承(私有继承(privateprivate)私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。【例Ex_PrivateDerived】派生类的私有继承示例程序运行结果如下:CMeter:20,CStick:10CMeter:21,CStick:1010 2.3.1 2.3.1 单继承单继承3 3保护继承(保护继承(protectedprotected)保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。表2.1列出了三种不同的继承方式的基类成员和其在
32、派生类中的特性。2.3.2 2.3.2 派生类的构造函数和析构函数派生类的构造函数和析构函数由于基类的构造函数和析构函数不能被派生类继承,因此,若有:CMeter oA(3);是可以的,因为CMeter类有与之相对应的构造函数。而CStickoB(3);是错误的,因为CStick类没有对应的构造函数。但CStickoC;是可以的,因为CStick类有一个隐含的不带参数的默认构造函数。【例Ex_ClassDerived】派生类的构造函数和析构函数的示例程序运行结果如下:调用CAnimal的构造函数!调用CCat的构造函数!猫的名字是:noname猫的名字是:Snoopy调用CCat的析构函数!调
33、用CAnimal的析构函数!2.3.3 2.3.3 多继承多继承多继承下派生类的定义按下面的格式:class :,;其中的继承方式还是前面提到的3种:public、private和protected。例如:class A/;class B/;class C:public A,private Bpublic A,private B/;2.3.4 2.3.4 虚基类虚基类【例Ex_Conflict】基类成员调用的二义性程序中,派生类B1和B2都从基类A继承,这时在派生类中就有两个基类A的拷贝。当编译器编译到“coutx=xendl;”语句时,因无法确定成员x是从类B1中继承来的,还是从类B2继承来
34、的产生了二义性,从而出现编译错误。解决这个问题的方法之一是使用域作用运算符“:”来消除二义性,例如若将print函数实现代码变为:void print()coutB1:x=B1:xendl;coutB1:x=B1:xendl;coutB2:x=B2:xendl;coutB2:x=B2:xendl;couty1=y1,y2=y2endl;coutz=zendl;重新运行,结果为:B1:x=200B2:x=400y1=100,y2=300z=5002.3.4 2.3.4 虚基类虚基类【例Ex_VirtualBase】基类成员调用的二义性程序运行结果如下:B1:x=0,y1=100B2:x=0,y2
35、=300z=500B1:x=400,y1=100B2:x=400,y2=300z=500从程序中可以看出:(1)声明一个虚基类的格式如下:virtual 其中,virtual是声明虚基类的关键字。声明虚基类与声明派生类一道进行,写在派生类名的后面。(2)在派生类B1和B2中只有基类A的一个拷贝,当改变成员x的值时,由基类B1和B2中的成员函数输出的成员x的值是相同的。(3)虚基类的构造函数的调用方法与一般基类的构造函数的调用方法是不同的。2.4 2.4 多态和虚函数多态和虚函数2.4.1 2.4.1 多态概述多态概述多态性可分为两种:编译时的多态性和运行时的多态性。编译时的多态性是通过函数或运
36、算符的重载来实现的。而运行时的多态性是通过虚函数来实现的,它指在程序执行之前,根据函数和参数还无法确定应该调用哪一个函数,必须在程序的执行过程中,根据具体的执行情况动态地确定。与这两种多态性方式相对应的是两种编译方式:静态联编和动态联编。2.4.2 2.4.2 虚函数虚函数【例Ex_VirtualFunc】虚函数的使用程序运行结果如下:678.53982.4.3 2.4.3 纯虚函数和抽象类纯虚函数和抽象类声明纯虚函数的一般格式为:virtual()=0;显然,它与一般虚函数不同的是:在纯虚函数的形参表后面多了个“=0”。把函数名赋为0,本质上是将指向函数的指针的初值赋为0。需要说明的是,纯虚
37、函数不能有具体的实现代码。抽象类抽象类是指至少包含一个纯虚函数的特殊的类。它本身不能被实例化,也就是说不能声明一个抽象类的对象。必须通过继承得到派生类后,在派生类中定义了纯虚函数的具体实现代码,才能获得一个派生类的对象。【例Ex_PureVirtualFunc】纯虚函数和抽象类的使用2.4.3 2.4.3 纯虚函数和抽象类纯虚函数和抽象类程序运行结果如下:6678.539878.53982.5 2.5 运算符重载运算符重载2.5.1 2.5.1 运算符重载函数运算符重载函数在类中,定义一个运算符重载函数与定义一般成员函数相类似,只不过函数名必须以operator开头,其一般形式如下::oper
38、ator()/函数体【例Ex_Complex】运算符的简单重载程序运行结果如下:实部=62,虚部=90实部=32,虚部=202.5.2 2.5.2 运算符重载限制运算符重载限制在C+中,运算符重载还有以下一些限制:(1)重载的运算符必须是一个已有的合法的C+运算符,如“+”、“-”、“*”、“/”、“+”等,且不是所有的运算符都可以重载。在C+中不允许重载的运算符有?:(条件)、.(成员)、*.(成员指针)、:(域作用符)、sizeof(取字节大小)。(2)不能定义新的运算符,或者说,不能为C+没有的运算符进行重载。(3)当重载一个运算符时,该运算符的操作数个数、优先级和结合性不能改变。(4)
39、运算符重载的方法通常有类的操作成员函数和友元函数两种,但=(赋值)、()(函数调用)、(下标)和-(成员指针)运算符不能重载为友元函数。2.5.3 2.5.3 友元重载友元重载友元重载方法既可用于单目运算符,也可以用于双目运算符,其一般格式如下:friend operator()/单目运算符重载 /函数体friend operator()/双目运算符重载 /函数体【例Ex_ComplexFriend】运算符的友元重载程序运行结果如下:实部=42,虚部=90实部=32,虚部=20实部=50,虚部=70实部=42,虚部=90实部=-12,虚部=-202.5.4 2.5.4 转换函数转换函数类型转换
40、是将一种类型的值映射为另一种类型的值。C+的类型转换包含自动隐含和强制转换两种方法。转换函数转换函数是实现强制转换操作的手段之一,它是类中定义的一个非静态成员函数,其一般格式为:class public:operator();/【例Ex_Money】转换函数的使用2.5.5 2.5.5 赋值运算符的重载赋值运算符的重载【例Ex_Evaluate】赋值运算符的重载程序运行结果如下:KeyMouse2.5.6 2.5.6 自增自减运算符的重载自增自减运算符的重载自增“+”和自减“-”运算符是单目运算符,它们又有前缀和后缀运算符两种。为了区分这两种运算符,在重载时应将后缀运算符视为双目运算符。即ob
41、j+或obj-应被看做:obj+0或obj-0又由于这里前缀“+”中的obj必须是一个左值,因此运算符重载函数的返回类型应是引用而不能是对象。设类为X,当用成员函数方法来实现前缀“+”和后缀“+”运算符的重载时,则可有下列一般格式:X&operator+();/前缀+X operator+(int);/后缀+若用友元函数方法来实现前缀“+”和后缀“+”运算符的重载时,则可有下列格式:friend X&operator+(X&);/前缀+friend X operator+(X&,int);/后缀+2.5.6 2.5.6 自增自减运算符的重载自增自减运算符的重载【例Ex_Increment】前缀
42、“+”和后缀“+”运算符的重载程序运行结果如下:981010122.6 2.6 输入输入/输出流输出流2.6.1 2.6.1 流类和流对象流类和流对象C+针对流的特点,构造了功能强大的输入/输出流库,它具有面向对象的特性,其继承结构如图2.2所示。2.6.2 2.6.2 流的格式控制和错误处理流的格式控制和错误处理1 1使用格式控制成员函数使用格式控制成员函数在ios类中控制输入/输出的成员函数有:int ios:width();/返回当前的宽度设置int ios:width(int);/设置宽度并返回上一次的设置int ios:precision();/返回当前的精度设置int ios:pr
43、ecision(int);/设置精度并返回上一次的设置char ios:fill();/返回当前空位填充的字符char ios:fill(char);/设置空位填充的字符并返回上一次的设置long ios:setf(long);/设置状态标志并返回上一次的状态标志long ios:unsetf(long);/消除状态标志并返回上一次的状态标志long ios:flags();/返回当前的状态标志long ios:flags(long);/设置状态标志并返回上一次的状态标志2.6.2 2.6.2 流的格式控制和错误处理流的格式控制和错误处理后面的4个成员函数都跟状态标志有关,各种状态值之间都是通
44、过“|”(或运算)组合而成的,在ios类中是一个公共的枚举类型,各个标志代表的含义如下:ios:skipws 跳过输入中的空白符ios:left 输出数据按输出域左对齐ios:right 输出数据按输出域右对齐(默认)ios:internal 数据的符号左对齐,数据本身右对齐,符号和数据之间为填充字符ios:dec 转换为十进制形式ios:oct 转换为八进制形式ios:hex 转换为十六进制形式ios:showbase 输出的数值前面带有基数符号(0或0 x)ios:showpoint 显示浮点数的小数点和后面的0ios:uppercase 用大写字母(AF)输出十六进制数ios:showp
45、os 显示正数前面的“+”号ios:scientific 按科学记数法显示浮点数ios:fixed 用定点格式显示浮点数ios:unitbuf 输入操作完成后立即刷新缓冲区ios:stdio 每次输入操作完成后刷新stdout和stderr 2.6.2 2.6.2 流的格式控制和错误处理流的格式控制和错误处理【例Ex_FormatFunc】使用格式控制成员函数程序运行结果如下:030071 +12345.70X3039 +1.234568E+004*This *is *a Test!This*is*a Test!*2.6.2 2.6.2 流的格式控制和错误处理流的格式控制和错误处理2 2使用格
46、式算子使用格式算子C+提供了一些格式算子来简化上述操作。格式算子是一个对象,可以直接用插入符或提取符来操作。C+提供的预定义格式算子如表2.2所示。【例Ex_Formator】使用格式算子2.6.2 2.6.2 流的格式控制和错误处理流的格式控制和错误处理3 3流的错误处理流的错误处理在ios类中,定义了一个公有枚举成员io_state来记录各种错误的性质:enum io_state goodbit=0 x00,/正常eofbit =0 x01,/已达到文件尾failbit=0 x02,/操作失败badbit =0 x04/非法操作;在ios类中又定义了检测上述流状态的下列成员函数:int i
47、os:rdstate();/返回当前的流状态,它等于io_state中的枚举值int ios:bad();/如果badbit位被置1,返回非0voidios:clear(int);/清除错误状态int ios:eof();/返回非0表示提取操作已到文件尾int ios:fail();/如果failbit位被置1,返回非0int ios:good();/操作正常时,返回非02.6.2 2.6.2 流的格式控制和错误处理流的格式控制和错误处理【例Ex_ManipError】检测流的错误#include int main()int i,s;char buf80;couti;s=cin.rdstate
48、();cout流状态为:hexsendl;while(s)cin.clear();cin.getline(buf,80);couti;s=cin.rdstate();return 0;2.6.2 2.6.2 流的格式控制和错误处理流的格式控制和错误处理程序运行结果如下:输入一个整数:a流状态为:2非法输入,重新输入一个整数:abcd非法输入,重新输入一个整数:122.6.3 2.6.3 使用输入使用输入/输出成员函数输出成员函数1 1输入操作的成员函数输入操作的成员函数数据的输入/输出可以分为三大类:字符类、字符串和数据。(1)使用get和getline函数用于输入字符或字符串的成员函数get
49、原型如下:int get();istream&get(char&rch);istream&get(char*pch,int nCount,char delim=n);第1种形式是从输入流中提取一个字符,并转换成整型数值。第2种形式是从输入流中提取字符到rch中。第3种形式是从输入流中提取一个字符串并由pch返回,nCount用来指定提取字符的最多个数,delim用来指定结束字符,默认时是n。函数getline原型如下:istream&getline(char*pch,int nCount,char delim=n);2.6.3 2.6.3 使用输入使用输入/输出成员函数输出成员函数【例Ex_G
50、etAndGetLine】get和getline的使用程序运行结果如下:请输入一个字符:A65请输入一行字符串:This is a test!This is a test!请输入一行字符串:ComputerComputer请输入一行字符串:你今天过得好吗?你今天过得好吗?2.6.3 2.6.3 使用输入使用输入/输出成员函数输出成员函数(2)使用read函数read函数不仅可以读取字符或字符串(称为文本流),而且可以读取字节流。其原型如下:istream&read(char*pch,int nCount);istream&read(unsigned char*puch,int nCount);
侵权处理QQ:3464097650--上传资料QQ:3464097650
【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。