《皮德常C++程序设计教程》Chapter-9_第1页
《皮德常C++程序设计教程》Chapter-9_第2页
《皮德常C++程序设计教程》Chapter-9_第3页
《皮德常C++程序设计教程》Chapter-9_第4页
《皮德常C++程序设计教程》Chapter-9_第5页
已阅读5页,还剩81页未读 继续免费阅读

下载本文档

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

文档简介

第9章类的高级局部皮德常南京航空航天大学计算机科学与技术学院1主要内容9.1静态成员9.2友元9.3对象赋值问题9.4拷贝构造函数9.5运算符重载9.6对象组合29.1静态成员例:一个学生类,定义其对象张三、李四,他们分别维护着类成员的一份副本〔学号、姓名、籍贯等〕。如果要统计一个班学生总数?用类外的变量记录,违背了数据封装。用类的一个数据成员记录,导致多个副本,不仅冗余,而且势必造成数据不一致。39.1.1静态数据成员1.用关键字static声明;2.同一个类中的所有对象都共享该变量;3.必须在类外定义和初始化,用(::)来指明所属的类。4.静态变量不依赖于对象而存在,无论是否认义该类的对象,这种类型的变量都存在。静态数据成员实际上是在类外定义的一个变量,它的生存期和整个程序的生存期一样,在定义对象之前,静态数据成员就已经存在。4classStaticDemo{staticintx;

inty;public:voidputx(inta){x=a; }voidputy(intb){y=b; }intgetx(){returnx; }intgety(){returny; }};intStaticDemo::x; //静态变量x将被StaticDemo类的所有对象共享,例如:StaticDemoobj1,obj2;obj1.putx(5);obj1.puty(l0);obj2.puty(20);cout<<"x:"<<obj1.getx()<<""<<obj2.getx()<<endl;cout<<"y:"<<obj1.gety()<<""<<obj2.gety()<<endl;理解它!59.1.2静态函数成员静态函数成员是类中的一个函数,有static修饰。静态函数成员和静态数据成员类似,在对象生成之前也已经存在。这就是说在对象产生之前,静态的函数成员就能访问其它静态成员。类外代码可以使用类名和作用域操作符来调用静态成员函数。静态成员函数只能引用属于该类的静态数据成员或静态成员函数。见例【例9-2】。6//budget2.h文件的内容。classBudget{ staticfloatcorpBudget; floatdivBudget;public: Budget(){divBudget=0;} voidaddBudget(floatb) { divBudget+=b; corpBudget+=divBudget; }

staticvoidmainOffice(float); floatgetDivBudget(){returndivBudget;} floatgetCorpBudget(){returncorpBudget;}};7//Contentsofbudget2.cpp#include"budget2.h"floatBudget::corpBudget=0; //Definitionofstaticmemberfunction.

voidBudget::mainOffice(floatmoffice){ corpBudget+=moffice;}8//主程序pr9-2.cpp的内容#include"budget2.h“voidmain(){ floatamount; inti; floatbud; cout<<"Entermainoffice'sbudgetrequest:"; cin>>amount;

Budget::mainOffice(amount); Budgetdivisions[4];9for(i=0;i<4;i++){ cout<<"EnterthebudgetforDivision"; cout<<(i+1)<<""; cin>>bud; divisions[i].addBudget(bud);}cout<<"\nHerearethedivisionbudget:\n";for(i=0;i<4;i++){ cout<<"\tDivision"<<(i+1)<<"\t$"; cout<<divisions[i].getDivBudget()<<endl;}10 cout<<"\tTotalRequests:"; cout<<divisions[0].getCorpBudget()<<endl;}1.对于静态的函数成员,是通过类名和作用域分辨符调用的。2.也可以采用对象点的方式调用budget2.h

budget2.cpp9-2.cpp119.2友元函数引入友元的原因?1.友元函数不是类中的函数成员,但它和类的函数成员一样,可以访问类中定义的私有成员。2.友元函数可以是一个外部函数,也可以是另外一个类的函数成员。3.将某个函数声明为一个类的友元方式,前面加friend。12【例9-3】求两个点之间的距离。classPoint{ intxPos,yPos;public: Point(intxx=0,intyy=0) {xPos=xx;yPos=yy;} intGetXPos(){returnxPos;} intGetYPos(){returnyPos;}

frienddoubleDistance(Point&a,Point&b);};9.2.1外部函数作为类的友元13doubleDistance(Point&a,Point&b){

doubledx=a.xPos-b.xPos; doubledy=a.yPos-b.yPos; returnsqrt(dx*dx+dy*dy);}voidmain(){Pointp1(3,5),p2(4,6);cout<<Distance(p1,p2)<<endl;}9-3.cpp不采用友元如何解决?14其他类的成员函数声明为一个类的友元函数,这个成员函数也称为友元成员。友元成员不仅可以访问自己所在类对象中的私有成员和公有成员,还可以访问friend声明语句所在类对象中的私有成员和公有成员,这样能使两个类相互合作完成某一任务。例:将Aux类的函数addBudget声明为Budget类的友元函数。9.2.2类的成员函数作为另外一个类的友元15classBudget;//对Budget类超前使用说明classAux //Aux类的定义{private:floatauxBudget;public:Aux(){auxBudget=0;}voidaddBudget(float,Budget&);floatgetDivBudget(){returnauxBudget;}};16classBudget//Budgetclassdeclaration{staticfloatcorpBudget;floatdivBudget;public:Budget(){divBudget=0;}voidaddBudget(floatB)

{divBudget+=B;corpBudget+=divBudget;}floatgetDivBudget(){returndivBudget;}floatgetCorpBudget(){returncorpBudget;}

staticvoidmainOffice(float);

friendvoidAux::addBudget(float,Budget&);};17//Contentsofbudget3.cpp#include"budget3.h" //DefinitionofstaticmemberofBudgetclassfloatBudget::corpBudget=0;//DefinitionofstaticmemberfunctionmainOffice.

voidBudget::mainOffice(floatmoffice){ corpBudget+=moffice;}18//Contentsofauxi1.cpp#include"auxi1.h"#include"budget3.h"//DefinitionofmemberfunctionmainOffice.voidAux::addBudget(floatb,Budget&div){ auxBudget+=b;

div.corpBudget+=auxBudget;}AttentionPlz19//Contentsofmainprogram#include"budget3.h“voidmain(){ floatamount; inti; floatbud; cout<<"Enterthemainoffice'sbudget:"; cin>>amount;

Budget::mainOffice(amount); Budgetdivisions[4];AuxauxOffices[4];20for(i=0;i<4;i++){cout<<"Enterthebudgetrequestfordivision"; cout<<(i+1)<<":"; cin>>bud; divisions[i].addBudget(bud); cout<<"Enterthebudgetrequestfordivision"; cout<<(i+1)<<"'sauxiliaryoffice:"; cin>>bud; auxOffices[i].addBudget(bud,divisions[i]);}21cout<<"Herearethedivisionbudgetrequests:\n";for(i=0;i<4;i++) {cout<<"\tDivision"<<(i+1)<<"\t\t\t"; cout<<setw(7); cout<<divisions[i].getDivBudget()<<endl; cout<<"\tAuxilaryOfficeofDivision"<<(i+1); cout<<"\t"; cout<<auxOffices[i].getDivBudget()<<endl;}cout<<"\tTotalRequests(includingmainoffice):";cout<<divisions[0].getCorpBudget()<<endl;}auxi1.hbudget3.hauxi1.cppbudget3.cpp9-3.cpp229.2.3一个类作为另外一个类的友元假设一个类为另一个类的友元,那么此类的所有成员都能访问对方类的私有成员。classA{intx;

friendclassB;public:voidDisplay(){cout<<x<<endl;}};classB{

Aa;public:voidSet(inti){a.x

=i;}voidDisplay(){a.Display();}};不好的方法,勿模仿239.3对象赋值问题采用赋值运算符“=”可以将一个对象赋值给另外一个对象,或者采用一个对象初始化另外一个对象。在缺省情况下,这两个操作执行的是对象成员之间的拷贝,也称为按位拷贝或浅拷贝。

【例9-5】浅拷贝应用举例。.24classRectangle{floatwidth,length,area;voidcalcArea(){area=width*length;}public:voidsetData(floatw,floatl){ width=w;length=l;calcArea();}floatgetWidth(){returnwidth;}floatgetLength(){returnlength;}floatgetArea(){returnarea;}};25voidmain(){ Rectanglebox1,box2; box1.setData(10,20); box2.setData(5,10); cout<<"\nBoxl'sArea:"<<box1.getArea(); cout<<"\nBox2'sArea:"<<box2.getArea(); box2=box1; //对象赋值 cout<<"\nBoxl'sArea:"<<box1.getArea(); cout<<"\nBox2'sArea:"<<box2.getArea();}9-5.cpp269.3对象赋值问题(continued)当采用一个对象初始化另一个对象时,对象成员之间的赋值也是按位拷贝。赋值和初始化的区别:赋值出现在两个对象都已经存在的情况下,而初始化出现在创立对象时,例如: Rectanglebox1; box1.setData(100,50); Rectanglebox2=box1;//initialization279.4拷贝构造函数拷贝构造函数是一个特殊的构造函数,当定义一个对象并采用同类型的另外一个对象初始化时,将自动调用拷贝构造函数。通常,采用缺省的按位拷贝操作也能正确地实现赋值,但在某些情况下不能正确执行。Example:

28classPersonInfo{ char*name; intage;public: PersonInfo(char*n,inta) { name=newchar[strlen(n)+1]; strcpy(name,n); age=a; }

~PersonInfo(){delete[]name;} char*getName(){returnname;} intgetAge(){returnage;}};PersonInfoperson1("Jones",20);PersonInfoperson2=person1;//将产生???291.解决的方法:定义拷贝构造函数。2.拷贝构造函数是一个特殊的构造函数,当采用一个对象初始化另一个对象时,将自动调用该函数。PersonInfo(PersonInfo&obj){ name=newchar[strlen(obj.name)+1]; strcpy(name,); age=obj.age;}拷贝构造函数的参数代表“=”运算符右边的对象:

PersonInfoperson2=person1;30进一步演化:UsingconstParametersPersonInfo(constPersonInfo&obj){ name=newchar[strlen(obj.name)+1]; strcpy(name,); age=obj.age;}319.4.1缺省的拷贝构造函数如果一个类没有定义拷贝构造函数,C++将为其创立一个缺省的拷贝构造函数。缺省的拷贝构造函数的功能就是按位赋值。321.用对象初始化同类的另一个对象。例如:

PersonInfost1("ZhangSan",20);

PersonInfost2(st1),st3=st1;9.4.2调用拷贝构造函数的情况Ex9-a.cpp332.如果函数的形参是对象,当进行参数传递时将调用拷贝构造函数。

voidchangePerson(PersonInfop){//…}

voidmain(){PersonInfost1("Susan",20);changePerson(st1); }思考:为什么拷贝构造函数的参数一定是个引用,而不是对象?Ex9-b.cpp343.如果函数的返回值是对象,函数执行结束时,将调用拷贝构造函数对无名临时对象初始化。

classPersonInfo{public:PersonInfo(){cout<<"调用构造函数\n";}PersonInfo(PersonInfo&obj){cout<<"调用拷贝\n";}~PersonInfo(){cout<<"调用析构函数\n";}};

PersonInfogetPerson(){PersonInfoperson;returnperson;//函数的返回值是对象}

voidmain(){PersonInfostudent;student=getPerson();}35注意:VC2005版本有问题,VC6正确。Ex9-c.cpp36如果函数返回值是对象,要考虑return语句的效率。例如: returnstring(s1+s2);表示“创立一个无名的临时对象并且将它返回”与“先创立一个局部对象temp并返回”不等价: stringtemp(s1+s2); returntemp;379.4.4编译器的一个纰漏classInvoiceItem{public: InvoiceItem(intsize=51)

{ cout<<"调用缺省构造函数!\n"; } InvoiceItem(char*d)

{ cout<<"调用一个参数的构造函数!\n";} InvoiceItem(char*d,intu)

{ cout<<"调用两个参数的构造函数!\n"; } InvoiceItem(InvoiceItem&obj)

{ cout<<"调用拷贝构造函数!\n"; } ~InvoiceItem()

{ cout<<"调用析构函数!\n"; }};389.4.4编译器的一个纰漏intmain(){ InvoiceItemInventory[5]={//对象数组

InvoiceItem("鼠标",100), //A行

InvoiceItem("硬盘"), //B行

"主板", //C行

99 //D行

}; return0;}注意:VC2005和VC6都不正确。9-ERROR.cpp399.5运算符重载Example1:The/operatorcanperformtwotypesofdivision:floatingpointandinteger.

Example2: Datetoday(2011,4,28); today.add(5); today+=5;//???409.5.1重载赋值运算符如果对象中有指针成员,采用拷贝构造函数能解决对象初始化问题,但并不能处理对象赋值。Example:假设PersonInfo具有拷贝构造函数:PersonInfoperson1("ZhangSan”,20),person2("John",24);person2=person1;//memberwiseassignment

41Overloadthe=operator:classPersonInfo{char*name;

intage;public: ……

voidoperator=(constPersonInfo&right){ delete[]name; name=newchar[strlen()+1]; strcpy(name,); age=right.age;}};

42operator=函数的参数不一定是常引用,上述声明优点:(1)效率高。采用引用可以防止参数传递时生成对象拷贝,节省了对象初始化和析构的过程。(2)将参数声明为常引用,可以防止函数无意间修改对象right的内容。(3)符合赋值运算的常识。函数调用:p2.operator=(p1);

p2=p1;p3=p2=p1;//???439.5.2this指针this是一个隐含的内嵌指针,它指向调用成员函数的当前对象。Example:

cout<<person1.getName()<<endl; cout<<person2.getName()<<endl;44//【例9-8】

classPersonInfo{ char*name; intage;public:PersonInfooperator=(constPersonInfo&right){ delete[]name; name=newchar[strlen()+1];strcpy(name,); age=right.age;return*this;}//其他函数略};45voidmain(){PersonInfojim("Jim",20),bob("Bob",21), clone=jim;clone=bob=jim;

//Calloverloaded=operatorcout<<clone.getName()<<"," <<clone.getAge()<<endl;}operator=还有问题吗?46PersonInfo&operator=(constPersonInfo&right){ //重载运算符“=”的秘笈:

//一、检查自赋值

if(this==&right)return*this;

//二、释放原有的内存空间

delete[]name;

//三、分配新的内存空间,并复制内容

name=newchar[strlen()+1];strcpy(name,);age=right.age;

//四、返回本对象的引用

return*this;}9-8.cpp47this指针是以隐含参数的形式传递给非静态的函数成员:

PersonInfo(char*name,intage)

{

this->name=newchar[strlen(name)+1];strcpy(this->name,name);

this->age=age;}489.5.3重载双目算术运算符引入原因:

Feetincheslength1(3,5),length2(6,3),length3; length3=length1+length2;//等价于

length3=length1.operator+(length2);仅讲述如何重载双目运算符+和-,其它类似。49classFeetInches{ intfeet,inches;voidsimplify();public://其他函数代码见节 ……FeetInchesoperator+(constFeetInches&);FeetInchesoperator-(constFeetInches&);};50//Overloadedbinary+operator.FeetInchesFeetInches::operator+( constFeetInches&right){ FeetInchestemp; temp.inches=inches+right.inches; temp.feet=feet+right.feet; temp.simplify(); returntemp;}51//Overloadedbinary-operator.FeetInchesFeetInches::operator-( constFeetInches&right){ FeetInchestemp; temp.inches=inches-right.inches; temp.feet=feet-right.feet; temp.simplify(); returntemp;}529.5.3重载双目算术运算符任何一个双目算术运算符B被重载以后,当执行二元运算时:Obj1BObj2完全等价于:Obj1.operatorB(Obj2)Ex9-d.cpp539.5.4重载单目算术运算符Example1:

distance2=++distancel;//前置++等价于: distance2=distancel.operator++();Example2:

distance2=distancel++;//后置++等价于: distance2=distancel.operator++(0);54classFeetInches//【例9-9】{ intfeet,inches;voidsimplify();public: ……FeetInchesoperator+(constFeetInches&);FeetInchesoperator-(constFeetInches&);

FeetInches

operator++();FeetInchesoperator++(int);};Dummyparameter55//Overloadedprefix++operator.FeetInchesFeetInches::operator++(){ ++inches; simplify(); return*this;}56 //Overloadedpostfix++operator.FeetInchesFeetInches::operator++(int){ FeetInchestemp(feet,inches); inches++; simplify(); return temp;}Ex9-e.cpp579.5.5重载关系运算符重载关系运算符,实现两个对象的比较,其中关系运算符函数要返回一个布尔值〔true或false〕:Example:if(distancel<distance2){ ...code...}58classFeetInches{intfeet,inches;voidsimplify();public: ……booloperator>(constFeetInches&);

booloperator<(constFeetInches&);

booloperator==(constFeetInches&);};59//Overloaded>operator.

bool FeetInches::operator>( constFeetInches&right){if(feet>right.feet) returntrue;elseif(feet==right.feet&&inches>right.inches) returntrue;elsereturnfalse;}60//Overloaded<operator.boolFeetInches::operator<( constFeetInches&right){if(feet<right.feet) returntrue;elseif(feet==right.feet&&inches<right.inches) returntrue;else returnfalse;}61//Overloaded==operator.boolFeetInches::operator==( constFeetInches&right){if(feet==right.feet&&inches==right.inches) returntrue;else returnfalse;}Ex9-f.cpp629.5.6重载流操作符<<和>>Example: intvalue; cout<<value; FeetInchesdistance;

cin>>distance;

注意:如果要为FeetInches类重载流插入符<<,那么必须通过友元函数的形式实现函数重载。能否实现?63classFeetInches{intfeet,inches;public: ……friend

ostream&operator<<(ostream&, FeetInches&);friendistream&operator>>(istream&, FeetInches&);};64//Overloaded<<operator.ostream

&operator<<( ostream&strm, FeetInches&obj){ strm<<obj.feet<<"feet," <<obj.inches<<"inches"; return strm;}65//Overloaded>>operator.istream &operator>>( istream&strm, FeetInches&obj){ cout<<"Feet:";strm>>obj.feet; cout<<"Inches:";strm>>obj.inches; return strm;}66

cout<<distancel<<""<<distance2<<endl;等价于如下过程:1.首先调用重载函数<<,执行cout<<distancel,返回cout对象;2.执行cout<<“”,返回值是cout对象;3.以(1)的方式,执行cout<<distance2;4.以(2)的方式,执行表达式中的cout<<endl;Ex9-g.cpp679.5.7重载类型转换运算符Example: inti=100; floatf=3.14f; i=f; f=i;对于一个对象,通过重载类型转换函数,可实现类型转换功能。

68classFeetInches{intfeet,inches;public: …… operatorfloat();

//Truncatestheinchesvalue operatorint(){ returnfeet; }};Noreturntypeisspecifiedinthefunctionheader.SincethefunctionisaFeetInches-to-floatconversionfunction,itwillalwaysreturnafloat.69

//ConvertaFeetInchesobjecttoafloat.

FeetInches::operatorfloat(){ floattemp=feet; temp+=(inches/12.0f); returntemp;}70voidmain(){ FeetInchesdistance; float f; int i; cin>>distance; f=distance; i=distance;}Ex9-h.cpp719.5.8重载[]操作符Example1: stringname="John"; cout<<name[0]; name[0]='a';C++除了支持重载传统的运算符,还支持重载[]符。这样就能象操作普通数组一样操作对象数组。72classIntArray{ int*aptr; intarraySize; voidmemError(); voidsubError();public: IntArray(int); IntArray(constIntArray&); ~IntArray(); intsize(){returnarraySize;} int&operator[](constint&);};why?73//ConstructorforIntArrayclassIntArray::IntArray(ints){ arraySize=s; aptr=newint[s]; if(aptr==0) memError(); for(inti=0;i<arraySize;i++) *(aptr+i)=0;}74//CopyConstructorforIntArrayclass.IntArray::IntArray(constIntArray&obj){ arraySize=obj.arraySize; aptr=newint[arraySize]; if(aptr==0) memError(); for(inti=0;i<arraySize;i++) *(aptr+i)=*(obj.aptr+i);}75//DestructorforIntArrayclass.IntArray::~IntArray(){ if(arraySize>0) delete[]aptr; arraySize=0;}76 //memErrorfunction.voidIntArray::memError(){ cout<<"Cannotallocatememory.\n"; exit(0);} //subErrorfunction.voidIntArray::subError(){ cout

温馨提示

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

评论

0/150

提交评论