拷贝构造函数及应用人员类电话用户服务类引用C++函数中参数的传递方式是传值。在函数域中为参数重新分配内存,而把实参的数值传递到新分配的内存中。它的优点是有效避免函数的副作用。如果要求改变实参的值,怎么办呢?如果实参是一个复杂的对象,重新分配内存会引起程序执行效率大大下降,怎么办呢?在C++中有一种新的导出型数据类型—引用(reference)可以解决上面的难题。引用又称别名(alias)。注意:1.对数组只能引用数组元素,不能引用数组(数组名本身为地址)。2.不能定义引用的引用(引用也是地址),所以当函数的参数为引用时,引用不能作实参。拷贝构造函数同一个类的对象在内存中有完全相同的结构,如果作为一个整体进行复制或称拷贝是完全可行的。这个拷贝过程只需要拷贝数据成员,而函数成员是共用的(只有一份拷贝)。在建立对象时可用同一类的另一个对象来初始化该对象,这时所用的构造函数称为拷贝构造函数(CopyConstructor)。CGoods类,拷贝构造函数为:CGoods(CGoods&cgd){Strcpy(Name,cgd.Name);Price=cgd.price;Amount=cgd.Amount;Totalvalue=cgd.Totalvalue;}拷贝构造函数的参数——采用引用。如果把一个真实的类对象作为参数传递到拷贝构造函数,会引起无穷递归。系统会自动提供称为缺省的按成员语义支持的拷贝构造函数,每个类成员被依次拷贝,亦称为缺省的按成员初始化。按成员作拷贝是通过依次拷贝每个数据成员实现的,而不是对整个类对象按位拷贝。赋值运算符“=”称缺省的按成员拷贝赋值操作符,同类对象之间可以用“=”直接拷贝。通常按成员语义支持已经足够。但在某些情况下,它对类与对象的安全性和处理的正确性还不够,这时就要求类的设计者提供特殊的拷贝构造函数和拷贝赋值操作符的定义。拷贝构造函数的另2个用途:1.当函数的形参是类的对象,调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参拷贝到新的对象中。2.当函数的返回值是类对象,函数执行完成返回调用者时使用。原因也是要存储在一个临时对象中,再返回调用者。因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以在处理这种情况时,编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。【例1】设计一个人员类(Person)设计一个学校在册人员类(Person)。数据成员包括:身份证号(IdPerson),姓名(Name),性别(Sex),生日(Birthday)家庭住址(HomeAddress)。成员函数包括人员信息的录入和显示。还包括构造函数与拷贝构造函数。设计一个合适的初始值。#includeiostream.h#includestring.henumTsex{mid,man,woman};//枚举类型classPerson{charIdPerson[19];//身份证号,18位数字charName[20];//姓名TsexSex;//性别intBirthday;//生日,格式1986年8月18日写作19860818charHomeAddress[50];//家庭地址public:Person();Person(char*,char*,Tsex,int,char*);Person(Person&);~Person();voidPrintPersonInfo();//打印信息voidinputPerson();//输入信息//其他接口函数};Person::Person(){//缺省构造函数cout缺省构造Personendl;IdPerson[0]='\0';Name[0]='\0';Sex=mid;Birthday=0;HomeAddress[0]='\0';}Person::Person(char*id,char*name,Tsexsex,intbirthday,char*homeadd){//带参构造函数cout构造Personendl;strcpy(IdPerson,id);strcpy(Name,name);Sex=sex;Birthday=birthday;strcpy(HomeAddress,homeadd);}Person::Person(Person&Ps){//拷贝构造函数cout拷贝构造Personendl;strcpy(IdPerson,Ps.IdPerson);strcpy(Name,Ps.Name);Sex=Ps.Sex;Birthday=Ps.Birthday;strcpy(HomeAddress,Ps.HomeAddress);}Person::~Person(){//析构函数cout析构Personendl;}voidPerson::inputPerson(){//输入信息charid[19];//身份证号,18位数字charname[20];//姓名Tsexsex;//性别intbirthday;//生日,格式1986年8月18日写作19860818charhomeadd[50];charch;cout请输入身份证号,18位数字:endl;cin.getline(id,19);cout请输入姓名:endl;cin.getline(name,20);定义变量输入变量值cout请输入性别m或w:endl;cinch;if(ch=='m')sex=man;elsesex=woman;cout请输入生日,格式1986年8月18日写作19860818:endl;cinbirthday;cout请输入地址:endl;cin.getline(homeadd,50);strcpy(IdPerson,id);strcpy(Name,name);Sex=sex;Birthday=birthday;strcpy(HomeAddress,homeadd);}输入变量值为成员赋值voidPerson::PrintPersonInfo(){//打印信息inti;cout“身份证号:”IdPerson‘\n’“姓名:”Name'\n'性别:;if(Sex==man)cout男'\n';elseif(Sex==woman)cout女'\n';elsecout'\n';cout出生年月日:;i=Birthday;couti/10000年;i=i%10000;couti/100“月”i%100“日”‘\n’“家庭住址:”HomeAddress'\n';}voidmain(){PersonPs1(“320102811226161”,“朱海鹏”,man,19811226,“南京市黄浦路1号”),Ps2(Ps1),Ps3;Ps1.PrintPersonInfo();Ps2.PrintPersonInfo();Ps3.inputPerson();Ps3.PrintPersonInfo();}【例2】设计一个电话用户类(Usetele)•数据成员为:–用户编号,电话号码,用户类型•成员函数为:–构造函数(初始化信息)–修改信息–打印信息–电话号码升位处理classUsetele{private:intid;//用户编号longnumber;//电话号码(七位,首位不为0和1)charutype;//用户类型(A-公用,B-私用)public:Usetele(){id=0;number=0;utype='';}Usetele(inta,longb,charc){id=a;number=b;utype=c;}Usetele(Usetele&g){//拷贝构造函数id=g.id+10;number=g.number;utype=g.utype;}voidmodify(inta,longb,charc){//修改信息id=a;number=b;utype=c;couta信息已修改!endl;}voidlist();//打印信息voidExpand();//电话号码升位处理};voidUsetele::list(){//打印信息cout用户编号:idendl;cout电话号码:numberendl;cout用户类型:utypeendl;}voidUsetele::Expand(){//电话号码升位处理cout电话号码number;if(number1e7)return;longa=number/1000000;//a取左边最高位数字if(utype=='A')switch(a){case2:case3:case4:case5:number=number+8e7;break;case6:case7:case8:case9:number=number+7e7;break;}elseswitch(a){case2:case3:case4:case5:number=number+6e7;break;case6:case7:case8:case9:number=number+5e7;break;}cout已升位为:numberendl;}voidmain(){Usetelea,b(3,3792345,'A');Usetelec(b),d(21,4466223,'B'),e(d);a.list();a.modify(6,8804455,'A');a.list();b.list();c.list();d.list();d.Expand();d.list();a.Expand();a.list();e.list();e=b;e.list();}