《C++面向对象程序设计》教学内容第1章C++概述第2章类和对象第3章面向对象程序设计概述第4章进一步学习类和对象第5章堆与复制构造函数第6章继承性:派生类第7章运算符重载第8章虚函数和多态性第9章模板第10章类库和C++的标准模板库STL第11章输入输出流第12章异常处理第4章进一步学习类和对象4.1对象数组4.2指向对象的指针4.3this指针4.4对象的赋值4.5对象作为函数参数4.6从函数返回对象4.7类的静态成员4.8类的友元4.1对象数组(Objectarrays)•定义:类名数组名[元素个数];例如:StudentaSA[10];//一个学生类对象数组•通过下标访问方法:数组名[下标].成员名;例如:aSA[j].print();对象数组初始化•数组中每一个元素对象被创建时,系统都会调用构造函数类初始化该对象。•通过初始化列表赋值。例:LocationA[2]={Location(1,2),Location(3,4)};•如果没有为数组元素指定显式初始值,数组元素使用缺省值初始化(调用缺省构造函数)。•当数组中每一个对象被删除时,系统都要调用一次析构函数。数组元素所属类的构造函数•没有自定义构造函数时,则采用缺省构造函数。•各元素对象的初值要求为相同的值时,可以定义出具有缺省形参值的构造函数。•各元素对象的初值要求为不同的值时,需要定义带形参(无缺省值)的构造函数。•如果需要定义动态数组,需要一个无参构造函数。对象数组例子#includeiostream.hclasssamp{inta,b;public:samp(intn,intm){a=n;b=m;}intget_a(){returna;}intget_b(){returnb;}};对象数组例子(续)main(){sampob[4][2]={samp(1,2),samp(3,4),samp(5,6),samp(7,8),samp(9,10),samp(11,12),samp(13,14),samp(15,16),};inti;对象数组例子(续)for(i=0;i4;i++){coutob[i][0].get_a();coutob[i][0].get_b()\n;coutob[i][1].get_a();coutob[i][1].get_b()\n;}cout\n;return0;}4.2指向对象的指针•可以定义指向对象的指针,在运用对象的指针的时候,对象的成员将用箭头运算符(→)而不是点运算符(.)引用。•对象的指针算法与其它数据类型的指针算法相同:它被与对象相联系的处理。例如:当对象指针增加的时候,它指向下一个对象;当对象指针减少的时候,它指向前一个对象。对象指针的用法intmain(){Circlec1(3),*pc;//pc为指向圆形Circle类对象的指针pc=&c1;//将对象c1的地址赋给对象指针pc,使其指向圆形对象c1coutc1.GetArea()endl;//通过对象名访问对象的方法coutpc-GetArea()endl;//通过对象指针访问对象的方法return0;}对象指针与对象数组#includestudent.hintmain(){…Student*sp=aSA;//给指针赋初值for(inti=0;i4;i++,sp++)sp-print();//指针加1后,指向下一个对象sp=sp-3;//指针减3后,指针向前移动三个对象sp-modify(87);sp-print();return0;}4.3this指针•对象是由数据和操作构成的一个整体,即使同一类的不同对象的操作也是相互独立的,各占不同的内存空间。但是,在C++实现中,这种处理方案太浪费内存空间。•同类的对象能否共享该类成员函数的同一个实例呢?myBirthday.set(31,12,1997);nationalDay.set(1.10,1949);voidDate::Set(intD,intM,inty){day=D;month=M;year=Y;}那么,此时计算机怎么能区分该函数是作用在哪个对象上呢?解决方案•C++在编译时,自动在每个成员函数中的第一个参数位置增加:X*constthis;当对象调用该函数时,编译器自动将该对象的指针作为实参传递给相应的函数,这样就可解决上述问题:voidDate::Set(Date*constthis,intD,intM,intY){this-day=D;this-month=M;this-year=Y;}注:程序员不能将this指针的说明写在函数中C的结构体struct与C++的struct的比较C语言的结构体中数据与操作是分离的在C++语言中将数据与操作封装在一个结构体中structStudent{intnumber;charname[15];floatscore;};数据成员structStudent{intnumber;charname[15];floatscore;voiddisplay(Student*this)//函数成员{cout”number:”this-number;cout”name:”this-name;cout”score:”this-scoreendl;}};/*独立函数display*/voiddisplay(Student*stu){printf(”number:%d”,stu-number);printf(”name:%s”,stu-name);printf(”score:%f\n”,stu-score);}数据成员this指针•this指针是由C++编译器自动产生而使用的一个隐含指针,类成员函数使用该指针来存取对象的数据成员。••定义同一类的多个对象时,每个对象拥有自己的数据成员但它们共用一份成员函数,当成员函数存取对象的数据时它们使用this指针,通过this指针指向不同对象来决定使用哪一个对象的数据成员。this指针是如何在“幕后”工作?例如complex类中的init函数:voidinit(doubler,doublei){real=r;image=i;}C++编译器在编译这个函数时自动插入this指针:voidinit(complex*this,doubler,doublei){this-real=r;this-image=i;}this指针的显式使用•this指针是系统的一个内部指针,通常以隐式方式使用,但也可以为程序员所使用,例如,当运算符重载和建立链表时this指针显得十分重要。•参见双向链表This指针的显式用法•显式地使用this指针,还可以使C++的编译器将同名的参数与数据成员区分开来:voidDate::Set(intday,intmonth,intyear){this-day=day;this-month=month;this-year=year;}4.4对象的赋值•如果两个对象属于同一种类型,那么我们可以将其中的一个对象的值(属性值)赋给另一个对象。•在默认情况下,当将一个对象赋值给另一个对象时,第一个对象的数据将被按位复制到第二个对象中。对象赋值的例子【例4.4】intmain(){Rectanglerect1(10,20),rect2(0,0);coutrect1beforeassignment:\n;rect1.show_Rect();coutrect2beforeassignment:\n;rect2.show_Rect();cout'\n';rect2=rect1;//将矩形对象rect1赋值给矩形对象rect2coutrect1afterassignment:\n;rect1.show_Rect();coutrect2afterassignment:\n;rect2.show_Rect();return0;}C++语言系统为Rectangle类自动提供的赋值运算符=4.5对象作为函数参数•在C++中,对象也可以象其它类型的数据一样,作为函数的参数。•对象作为函数的参数时,实际参数与形式参数的传递方式也有两种:–传送数值(传值调用CallbyValue)–传送地址(传址调用CallbyReference)传值调用CallbyValue•传值调用时,传送的是作为实际参数的对象的拷贝而不是实际对象本身。•因此函数中对对象的任何修改均不影响作为实际参数的对象本身。•创建形式参数对象时,是通过类的复制构造函数完成的。传值调用例子【例4.5】classSquare{intside;public:voidset(intx){side=x;}voidout(){coutside\n;}};voidf(Squarex)//采用传值方式将实际对象传送给临时对象x{x.out();//输出正方形Square类对象x的边长为10x.set(100);//这仅仅修改了临时对象x的边长x.out();//输出正方形Square类对象x的边长为100}传值调用例子(续)intmain(){Squares;s.set(10);f(s);s.out();//输出正方形对象s的边长仍然为10,边长没有改变return0;}对象s以传值方式传送给临时对象传址调用CallbyReference•传址调用时,传递的是作为实际参数的对象的地址;•在函数中对作为形式参数的对象的修改实际上就是对作为实际参数的对象的修改。•传址调用时,即可以使用指针,也可以使用引用。传址调用例子【例4.6】classSquare{intside;public:voidset(intx){side=x;}voidout(){coutside\n;}};voidf(Square&x)//对象的引用作为函数的参数{x.out();//输出正方形对象x的边长为10x.set(100);//这实际上就是对作为实际参数的对象s的修改x.out();//输出正方形对象x的边长为100}传址调用例子(续)intmain(){Squares;s.set(10);f(s);//传送对象s的引用给x,x和s代表同一个对象s.out();//输出正方形对象s的边长为100,边长改变了return0;}传送对象s的地址4.6从函数返回对象•函数也可以返回对象。•要从函数返回一个对象,首先必须将函数的返回类型声明为一个类类型。•在函数结束之前用return语句返回该类的一个对象。【例4.8】函数返回对象myStringinput()//函数的返回类型为myString{charinstr[80];myStringstr;coutEnterastring:;cininstr;str.set(instr);returnstr;//返回一个myString类型的对象}【例4.8】函数返回对象(续)intmain(){myStringob;//将返回的对象复制给对象obob=input();ob.print();return0;}4.7类的静态成员(staticmembers)•一、静态数据成员•二、静态成员函数4.7.1静态数据成员•声明类的静态数据成员的方式是在变量声明的前面加static关键字。classPeople{private:staticintcount;intage;char*name;public:staticinttotalAge;people(intold,char*string);…};静态数据成员的初始化#includeiostream.hstaticintPeople::count=0;staticintPeople::totalAge=0;main(){Peoplemember(32,“Smith”)’Peoplemember(18,“John”);…}注意:在用People类生成任何对象之前,必须对其中的静态数据成员单独初始化。静态成员的访问•当通过