第三章类和对象2主要内容效率和健壮性通过引用来传递和返回对象const:const类型参数的对象引用、const成员函数示例程序:时间标记类构造函数的进一步讨论:拷贝构造函数示例程序:Point类和ArrayPoint类效率和健壮性——通过引用来传递和返回对象一般来说应该采用引用方式进行对象的传递和返回,而不要采用传值的方式来进行传值方式来传递和返回对象时会降低效率并将面临对象间的拷贝操作。例:通过引用来传递参数#includeiostreamusingnamespacestd;classC{public:voidset(intn);intget()const;private:intnum;};voidC::set(intn){num=n;}intC::get()const{returnnum;}voidf(C&c);{c.set(-999);coutc.get()'\n';}intmain(){Cc1;f(c1);return0;}f函数修改的是c1的实际内容,而非c1的一个临时拷贝引用返回引用返回的语法是在返回类型前加一个标记例如:int&val(){//…returni;}这种情况下,返回值不再拷贝到临时存储空间,甚至连return语句所用的那个存储单元对调用者而言都是可访问的。示例:引用返回许多程序员习惯于从1开始的数组,如下函数实现了这一功能。int&newIndex(inta[],inti){returna[i-1];}好处:若函数以引用方式返回,则这个函数调用可出现在赋值语句的左边合法调用:newIndex(a,8)=-16;原因:使用引用返回的是一个实际单元使用注意:必须保证函数返回时该单元仍然有效。效率和健壮性——constconst类型参数的对象引用示例:classC{public:voidsetName(conststring&n){name=n;}//…};如果一个对象通过引用方式传到函数中,而函数又不会修改对象的数据成员的值,那么最好将的参数标记为const效率和健壮性——constconst成员函数如果一个成员函数不需要改变该函数所属对象的任何数据成员,最好将这个成员函数标记为const示例:classC{public:voidset(conststring&n);conststring&get()const;private:stringname;};voidC::set(conststring&n){name=n;}constC::string&get()const;{returnname;}set函数中不改变参数n的值,用const;get函数不改变数据成员name的值,用const;get函数返回数据成员name的一个const引用,表示不能通过引用修改数据成员name的值还有关于const——常对象在定义对象时指定对象为常对象。常对象必须要有初值,如Timeconstt1(12,20,30);//t1是常对象对象t1中的所有成员的值都不能被修改。凡希望保证数据成员不被改变的对象,可以声明为常对象。定义常对象的一般形式为类名const对象名[(实参表列)];或const类名对象名[(实参表列)];Timeconstt1(12,20,30);constTimet1(12,20,30);注意:常对象不能调用该对象的非const型的成员函数(除了由系统自动调用的构造函数和析构函数)。constTimet1(10,15,36);//定义常对象t1t1.get_time();//get_time()为非const型成员函数,调用非法若要引用常对象中的数据成员,只需将该成员函数声明为const即可。voidget_time()const;//将函数声明为const常成员函数可以访问常对象中的数据成员,但仍然不允许修改常对象中数据成员的值。示例程序:时间标记类问题:时间标记是一个数值,用来记录某个瞬间时间。如用一个时间标记记录到货时间,用另一个时间标记记录发款时间。创建一个TimeStamp类完成如下操作事件发生时记录一个时间标记将时间标记作为整数打印将时间标记作为字符串打印将一个时间标记分解为年、月、日、小时、分钟和秒,并分别打印测试程序如下:voiddumpTS(constTimeStamp&ts){cout\nTestingmethods:\n;cout'\t'ts.get()'\n';cout'\t'ts.getAsString()'\n';cout'\t'ts.getYear()'\n';cout'\t'ts.getMonth()'\n';cout'\t'ts.getDay()'\n';cout'\t'ts.getHour()'\n';cout'\t'ts.getMinute()'\n';cout'\t'ts.getSecond()'\n';}#includeiostreamusingnamespacestd;voiddumpTS(constTimeStamp&);intmain(){TimeStampts;time_tnow=time(0);ts.set();dumpTS(ts);ts.set(now+200000);dumpTS(ts);ts.set(now-300000);dumpTS(ts);ts.set(-999);dumpTS(ts);return0;}运行结果:解决方案使用标准库中的time和ctime函数,这两个函数的原型在头文件ctime中。time函数以算术值的形式返回当前时间time返回的值是从某个特定时间到现在为止的秒数,如1970年1月1日零点到现在time原型为:time_ttime(time_t*ptr);ctime可将从time函数返回的值转换成可读的字符串如WedMar2417:06:382010ctime原型为:char*ctime(consttime_t*ptr);通常的用法:time_tnow=time(0);coutnow‘\n’ctime(&now)‘\n’;输出:1269421598WedMar2417:06:382010字符串格式的含义最前面三个字符表示星期几第五个字符到第七个字符表示月份第九个和第十个字符表示是该月的第几天第十二和十三个字符表示小时第十五和十六个字符表示分钟第十八和十九个字符表示秒第二十一到二十四字符表示年份程序清单#ifndefTIMESTAMP//TimeStamp.h#defineTIMESTAMP#includestring#includectimeusingnamespacestd;classTimeStamp{public:voidset(longs=0);time_tget()const;stringgetAsString()const;stringgetYear()const;stringgetMonth()const;stringgetDay()const;stringgetHour()const;stringgetMinute()const;stringgetSecond()const;private:stringextract(intoffset,intcount)const;time_tstamp;};#endif//TimeStamp.cpp#includeTimeStamp.hstringTimeStamp::extract(intoffset,intcount)const{stringtimeString=ctime(&stamp);returntimeString.substr(offset,count);}voidTimeStamp::set(longs){if(s=0)stamp=time(0);elsestamp=s;}time_tTimeStamp::get()const{returnstamp;}stringTimeStamp::getAsString()const{returnextract(0,24);}stringTimeStamp::getYear()const{returnextract(20,4);}stringTimeStamp::getMonth()const{returnextract(4,3);}stringTimeStamp::getDay()const{returnextract(8,2);}stringTimeStamp::getMinute()const{returnextract(14,2);}stringTimeStamp::getHour()const{returnextract(11,2);}stringTimeStamp::getSecond()const{returnextract(17,2);}动手练一练:创建一个支持int型的压入和弹出操作的堆栈类解决方案:公有成员函数对Stack对象进行初始化检查Stack为空或已满将整数压入到Stack中从Stack里弹出整数不移出任何元素,将Stack内容输出到标准输出私有成员一个用于打印错误信息的私有成员函数两个私有数据成员#includeiostreamusingnamespacestd;classStack{public:enum{MaxStack=5};voidint();voidpush(intn);intpop();boolisEmpty();boolisFull();voiddump();//输出栈顶到栈底的元素private:voiderrMsg(constchar*msg)const{cout\n***Stackoperationfailure:msg'\n‘;}inttop;intarr[MaxStack];};请编写类的实现文件和测试文件构造函数的进一步讨论:拷贝构造函数有时需要用到多个完全相同的对象用一个已有的对象快速地复制出多个完全相同的对象。如:TimeStampts1(ts);其作用是用已有的对象ts去克隆出一个新对象ts1其一般形式为:类名对象2(对象1);作用:用对象1复制出对象2建立对象时调用一个特殊的构造函数——拷贝构造函数(copyconstructor)。拷贝构造函数也是构造函数,但它只有一个参数,这个参数是本类对象的引用例拷贝构造函数举例classPoint{public:Point(intxx=0,intyy=0){X=xx;Y=yy;}Point(Point&p);intGetX(){returnX;}intGetY(){returnY;}private:intX,Y;};Point::Point(Point&p){X=p.X;Y=p.Y;cout拷贝构造函数被调用endl;}intmain(){PointA(1,2);PointB(A);//拷贝构造函数被调用coutB.GetX()endl;}例拷贝构造函数举例当用类的一个对象去初始化该类的另一个对象时系统自动调用拷贝构造函数实现拷贝赋值。例拷贝构造函数举例若函数的形参为类对象,调用函数时,实参赋值给形参,系统自动调用拷贝构造函数。例如:voidfun1(Pointp){coutp.GetX()endl;}intmain(){PointA(1,2);fun1(A);//调用拷贝构造函数}例拷贝构造函数举例当函数的返回值是类对象时,系统自动调用拷贝构造函数。Pointfun2(){PointA(1,2);returnA;//调用拷贝构造函数}intmain(){PointB;B=fun2();}拷贝构造函数如果程序员没有为类声明拷贝初始化构造函数,则编译器自己生成一个默认的拷贝构造函数。功能:把初始值对象的每个数据成员的值都复制到新建立的对象中。C+