1第4章类和对象(二)前一章讲述了类和对象的若干基础知识,本章接着进一步对类和对象的其它方面内容进行讨论。包括:指针、引用和数组;常类型在类和对象方面的应用;使用new和delete运算符对对象进行动态分配和释放。本章将通过例子进一步熟悉类和对象在编程中的应用,从而进一步理解类和对象的作用。§4.1对象指针和对象引用本节讲述:指向对象的指针和对象的引用的概念及在C++编程中的应用。一、指向类的成员的指针在C++中,可以说明指向类的数据成员和成员函数的指针。1.指向数据成员的指针格式:类型说明符(类名::*指针名)2.指向成员函数的指针格式:类型说明符(类名::*指针名)(参数表)例如:设有如下一个类A:classA{public:intfun(intb){returna*c+b;}A(inti){a=i;}intc;private:inta;};·定义一个指向类A的数据成员c的指针pc,其格式如下:intA::*pc=&A::c;//c是公有成员,在程序中可以这样定义。·定义一个指向类A的成员函数fun的指针pfun,其格式如下:2intA::*pfun(int)=A::fun;由于类不是运行时存在的对象。因此,在使用这类指针时,需要首先指定A类的一个对象,然后,通过对象来引用指针所指向的成员。例如,给pc指针所指向的数据成员c赋值8,可以表示为:Aa;a.c=8a.*pc=8;//.*—用来对指向类成员的指针来操作该类的对象的。如果使用指向对象的指针来对指向类成员的指针进行操作时,使用-*运算符。例如:A*p=&a;//a是类A的一个对象,p是指向对象a的指针。p-*pc=8;3.指向一般函数的指针的定义和调用:·指向一般函数的指针的定义格式:类型说明符*指向函数的指针名(参数表)·给指向函数的指针的赋值格式:指向函数的指针名=函数名·在程序中,使用指向函数的指针调用函数的格式:(*指向函数的指针名)(实参表)如果是指向类的成员函数的指针还应加上相应的对象名和对象成员运算符。[例4.1]分析下列程序的输出结果#includeiostream.hclassA{public:A(inti){a=i;}intfun(intb){returna*c+b;}b=5intc;private:inta;};3voidmain(){Ax(8);a=8intA::*pc;//pc—指向类A的数据成员c的指针pc=&A::c;x.*pc=3;c=3//对对象x的c成员赋值,x.c=3;int(A::*pfun)(int);//pfun—指向类A的成员函数fun()的指针pfun=A::fun;A*p=&x;//p—指向类A的对象x的指针cout(p-*pfun)(5)endl;//通过指向对象的指针p来调用指向类的成员函数的指针pfun,即使用指向对象的指针调用指向类的成员函数的指针所指向的成员函数。p-fun(5)x.fun(5)}执行该程序输出如下结果:29(a=8,b=5,c=3)说明:1.该程序中,定义了一个指向类的数据成员的指针pc和一个指向类的成员函数的指针pfun,并且对这个指针进行了赋值,请注意赋值的格式。2.程序中对指向类的成员的两个指针进行了引用和调用。其中:x.*pc=3;实际上是对象x的C成员赋值,它是一个公有成员。等价于:x.c=3;而(p-*fun)(5)通过指向对象的指针p来调用指向类的成员函数的指针pfun,函数的实参为5。这里要注意其调用格式。这是一个使用指向对象的指针调用指向类的成员函数的指针所指向的成员函数。它等价于:p-fun(5)也可以使用对象来调用指向类的成员函数的指针所指向的成员函数。例如:(x.*pfun)(5)43.程序中使用了两种不同的指针。一种是指向对象的指针,程序中定义如下:A*p=&x;其中,p是指向类A的对象x的指针,x是事先定义好的类A的一个对象。这里,使用某个对象的地址值对指向对象的指针初始化,在给指向对象的指针赋值或赋初值时一定要注意是相同类的。另一种是指向类的成员的指针,前面讲过了,它们是pc和pfun,分别指向类A的数据成员c和成员函数fun()。虽然它们都是指针,但是所指向的对象是不相同的,它们的值也是不相同的。p——指向类的对象;pc——指向类的数据成员;pfun——指向类的成员函数。二、对象指针和对象引用作函数参数1.对象指针作函数参数使用对象指针作为函数参数要比使用对象作函数参数更普遍一些。因为使用对象指针作函数参数有如下两点好处:(1)实现传址调用。可在被调用函数中改变调用函数的参数对象的值,实现函数之间的信息传递。(2)使用对象指针实参仅将对象的地址值传给形参,而不进行副本的拷贝,这样可以提高运行效率,减少时空开销。对象指针作函数形参时,要求调用函数的实参是对象的地址值。即:形参——对象指针实参——对象的地址值下面通过一个例子来说明使用指向对象的指针作函数参数的调用方法。[例4.2]分析下列程序的输出结果。5#includeiostream.hclassM{public:M(){x=y=0;}M(inti,intj){x=i;y=j;}voidcopy(M*m);voidsetxy(inti,intj){x=i;y=j;}voidprint(){coutx”,”yendl;}private:intx,y;};voidM::copy(M*m)//形参为指向对象的指针{x=m-x;y=m-y;}voidfun(Mm1,M*m2);voidmain(){Mp(5,7),q;p:x=5,y=7;q:x=0,y=0q.copy(&p);//实参为对象的地址值x=5,y=7fun(p,&q);p.print();//x=5,y=7q.print();//x=22,y=25}voidfun(Mm1,M*m2){m1.setxy(12,15);//x=12,y=15m2-setxy(22,25);//x=22,y=25}执行该程序输出如下结果:5,722,256说明:(1)该程序中,有两个指向对象的指针。一个是用作成员函数copy()参数,另一个是用作一般函数fun()指针。当形参是指向对象的指针时,调用函数的对应实参应该是某个对象的地址值,一般使用&后加对象名。(2)在fun()函数中,两个形参,一个是对象名,另一个是指向对象的指针名。当在被调用函数中,改变了对象的数据成员值和指向对象指针的数据成员值以后,可以看到只有指向对象指针作参数所指向的对象被改变了,而另一个以对象作参数,形参对象值改变了,可实参对象值并没有改变。因此,该程序将出现上述的输出结果。2.对象引用作函数参数在实际中,使用对象引用作函数参数要比使用对象指针作函数参数更普遍。这是因为使用对象引用作函数参数具有用对象指针作函数参数的优点,而用对象引用作函数参数将更简单,更直接。所以,在C++编程中,人们喜欢用对象引用作函数参数。下面通过一个例子看一下如何使用对象引用作函数参数。该例是在例4.2的程序中,将对象指针改为对象引用而编写的。读者可以对比一下两者在使用上有何区别。[例4.3]分析下列程序的输出结果。#includeiostream.hclassM{public:M(){x=y=0;}M(inti,intj){x=i;y=j;}voidcopy(M&m);voidsetxy(inti,intj){x=i;y=j;}voidprint(){coutx,yendl;}private:intx,y;7};voidM::copy(M&m){x=m.x;y=m.y;}voidfun(Mm1,M&m2);voidmain(){Mp(5,7),q;q.copy(p);fun(p,q);p.print();q.print();}voidfun(Mm1,M&m2){m1.setxy(12,15);m2.setxy(22,25);}执行该程序的输出结果与例4.2完全一样。说明:该程序与例4.2比较可以看出,主要区别在于例4.2中使用的指向对象的指针在例4.3中换成了对象引用。请读者比较一下指针与引用在使用上的区别。三、this指针this是一个隐含于每一个类的成员函数中的特殊指针。该指针是一个指向正在被某个成员函数操作的对象的指针。当对一个对象调用成员函数时,编译程序先将对象的地址赋给this指针,然后调用成员函数,每次成员函数存取数据成员时,则隐含使用this指针。而8通常不去显式地使用this指针来引用数据成员。同样也可以使用*this来标识调用该成员函数的对象。下面通过一个例子来说明this指针的用法。[例4.4]分析下列程序输出结果。说明程序中this和*this的用法。#includeiostream.hclassA{public:A(){a=b=0;}A(inti,intj){a=i;b=j;}voidcopy(A&aa);voidprint(){couta”,”bendl;}private:inta,b;};voidA::copy(A&aa){if(this==&aa)return;//this——操作该成员函数的对象的地址*this=aa;//*this——操作该成员函数的对象,表示将形参aa获得的某对象的值赋给操作该成员函数的对象——a1}voidmain(){Aa1,a2(3,4);a1.copy(a2);a1.print();}执行该程序输出如下结果:3,4说明:在该程序中,类A的成员函数copy()内,出现了两次this指针。其中,this是操作该成员函数的对象的地址,从main()中可见操作该成员函数的对象是a1。*this是操作该成员函数的对象,而9*this=aa;表示将形参aa获得的某对象的值赋给操作该成员函数的对象,在该例中,操作该成员函数的对象为a1。§4.2数组本节中讲述对象与数组的关系,前面看见过数组可以作为类的成员,本节要讲述对象作数组的元素。本节还要讲述指向对象数组的指针和对象指针数组等内容。一、对象数组对象数组是指数组元素为对象的数组。该数组中若干个元素必须是同一个类的若干个对象。对象数组的定义、赋值和引用与普通数组一样,只是数组的元素与普通数组不同,它是同类的若干个对象。1.对象数组的定义对象数组定义格式如下:类名数组名[大小]…其中,类名——指出该数组元素是属于该类的对象,大小——给出某一维的元素个数。一维对象数组只有一个方括号,二维对象数组要有两个方括号,等等。例如:DATEdates[7];表明dates是一维对象数组名,该数组有7个元素,每个元素都是类DATE的对象。又例如:DATEdates2[3][5];这里,dates2是一个二维对象数组的名字,它有15个属于DATE类的对象。2.对象数组的赋值对象数组可以被赋初值,也可以被赋值。下面看一个例子。10classDATE{public:DATE(intm,intd,inty);voidprintf();private:intmonth,day,year;};DATEdates[4]={DATE(7,22,1998),DATE(7,23,1998),DATE(7,24,1998),DATE(7,25,1998)}或者dates[0]=DATE(7,22,1998);dates[1]=DATE(7,23,1998);dates[2]=DATE(7,24,1998);dates[3]=DATE(7,25,1998);对象数组元素的下标也是从0开始。对象数组的赋值和一般数组赋值一样,也是给每个元素赋值。下面通过一个例子进一步讲述对象数组的赋初值和赋值方法以及对象数组的引用。[例4.5]分析下列程序的输出结果。#includeiostream.hclassDATE{public:DATE(){month=day=year=0;}DATE(i