复制构造函数隐含的复制构造函数使用:1.当函数的形参是类对象,调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参复制到新的对象中。2.当函数的返回值是类对象,函数执行完成返回调用者时使用。理由也是要建立一个临时对象,再返回调用者。因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。所谓return对象,实际上是调用复制构造函数把该对象的值拷入临时对象。如果返回的是变量,处理过程类似,只是不调用构造函数。成员对象与构造函数聚合(aggregation):类中的成员,除了成员数据和成员函数外,还有成员对象,即用其他类的对象作为类的成员。使用成员对象的技术称为聚合。成员对象是实体,系统不仅为它分配内存,而且要进行初始化。【例4.6】含有成员对象的类的构造函数成员对象与构造函数含对象成员的构造函数:类名::构造函数名(参数总表):对象成员1(参数名表1),对象成员2(参数名表2),……对象成员n(参数名表n){……}冒号后用逗号隔开的为要初始化的对象成员,附在后面的参数名表1,…,参数名表n依次为调用相应对象成员所属的构造函数时的实参表。这些表中的参数通常来自冒号前的参数总表,但没有类型说明。含对象成员的类对象的初始化时,首先依次自动调用各成员对象的构造函数,再执行该类对象自己的构造函数的函数体部分。各成员对象的构造函数调用的次序与类定义中说明的顺序一致,而与它们在构造函数成员初始化列表中的顺序无关。成员对象与构造函数含对象成员的析构函数:因为析构函数没有参数,所以包含成员对象的类的析构函数形式上并无特殊之处。但是撤销该类对象时,会首先调用自己的析构函数,再调用成员对象的析构函数,调用次序与初始化时的次序相反。成员对象与构造函数构造函数另一格式:对于不含对象成员的类对象的初始化,也可以套用以上的格式,把部分需要直接赋初值的变量初始化写在冒号的右边:类名::构造函数名(参数表):变量1(初值1),……,变量n(初值n){……}当然也可以把一部分变量重新放回花括号中的函数体。冒号以后部分实际是函数体的一部分,所以在构造函数的声明中,冒号及冒号以后部分必须略去。成员对象与构造函数构造函数和析构函数的调用规则:1.对全局定义的对象,当程序进入入口函数main之前对象就已经定义,那时要调用构造函数。整个程序结束时调用析构函数。2.对于局部定义的对象,每当程序控制流到达该对象定义处时,调用构造函数。当程序控制走出该局部域时,则调用析构函数。3.对于静态局部定义的对象,在程序控制首次到达该对象定义处时,调用构造函数。当整个程序结束时调用析构函数。成员对象与构造函数在正确定义了构造函数和析构函数的前提下,在一个健康的程序中,每个创建的对象必然有一个而且只有一个撤消动作。注意:先建立的对象后撤销。【例4.7】演示对象创建和撤消的对应关系演示对象创建和撤消的对应关系本例目的是总结一下语法,请注意各函数输出的标志:classcomplex{private:doublereal,image;public:complex(){//默认的构造函数real=0.0;image=0.0;coutInitializing00endl;}complex(doubler,doublei=0.0){//带参数的构造函数real=r;image=i;coutInitializingr'\t'iendl;}complex(complex&com);//复制的构造函数声明~complex(){//析构函数coutDestructorendl;}演示对象创建和撤消的对应关系本例目的是总结一下语法,请注意各函数输出的标志:classcomplex{private:doublereal,image;public:complex(){//默认的构造函数real=0.0;image=0.0;coutInitializing00endl;}complex(doubler,doublei=0.0){//带参数的构造函数real=r;image=i;coutInitializingr'\t'iendl;}complex(complex&com);//复制的构造函数声明~complex(){//析构函数coutDestructorendl;}演示对象创建和撤消的对应关系voidassign(complexcom){real=com.real;//先建立临时对象comimage=com.image;}voidprint(){coutreal'+'image'i'endl;}};inlinecomplex::complex(complex&com){//复制的构造函数说明coutCopycom.real'\t‘com.imageendl;real=com.real;image=com.image;}演示对象创建和撤消的对应关系complexfun(complexcom){coutEnteringfunctionendl;global.assign(com);coutExitingfunctionendl;returnglobal;}complexglobal;//全局对象首先建立intmain(){coutEnteringmainendl;complexcom1,com2(5.6,7.5);complexcom3=com1;com3.print();global.print();com1=fun(com2);com1.print();coutExitingmainendl;return0;}演示对象创建和撤消的对应关系运行结果:Initializing00//全局对象global建立,调默认的构造函数Enteringmain//进入入口函数mainInitializing00//用默认的构造函数建立com1Initializing5.67.5//用带参数的构造函数建立com2Copy00//用复制的构造函数建立com30+0i//打印com30+0i//打印globalCopy5.67.5//调用全局函数fun(),调用复制构造函数建立//局部对象,fun()中的形参comEnteringfunction//进入全局函数fun()Copy5.67.5//进入global.assign(),调用复制构造函数//建立局部对象,assign()中的形参com演示对象创建和撤消的对应关系Destructor//退出global.assign(),调用析构函数,//清assign()中的形参comExitingfunction//将退出fun()Copy5.67.5//返回对象时调用复制构造函数建立临时对象Destructor//退出fun(),调用析构函数,清fun()的comDestructor//返回的临时对象赋给com1后析构5.6+7.5i//打印com1Exitmain//将退出入口函数mainDestructor//退出入口函数前,调用析构函数,清com3Destructor//退出入口函数前,调用析构函数,清com2Destructor//退出入口函数前,调用析构函数,清com1Destructor//退出入口函数前,调用析构函数,清global本例运行结果应与程序对比,看看程序运行的细节。运算符的重载const引用进一步说明:引用在内部存放的是被引用对象的地址,不可寻址的值是不能引用的;当引用作为形参时,实参也不能使用不可寻址的值,更不可能进行类型转换(如:实数转换为整数)。但是const引用不同,它是只读的,为了绝对保证不会发生误改,编译器实现const引用时,生成一个临时对象,引用实际上指向该临时对象,但用户不能访问它。所以const引用可以实现不可寻址的值(包括字面常量)的引用。例如:doubledval=1024;constint&ri=dval;是正确的,编译器将其转换为:doubledval=1024;inttemp=dval;constint&ri=temp;因有临时对象,引用和类型转换都实现了。当const引用作为形参时,实参也能使用不可寻址的值,并能进行类型转换。运算符的重载const引用进一步说明:引用在内部存放的是被引用对象的地址,不可寻址的值是不能引用的;当引用作为形参时,实参也不能使用不可寻址的值,更不可能进行类型转换(如:实数转换为整数)。但是const引用不同,它是只读的,为了绝对保证不会发生误改,编译器实现const引用时,生成一个临时对象,引用实际上指向该临时对象,但用户不能访问它。所以const引用可以实现不可寻址的值(包括字面常量)的引用。例如:doubledval=1024;constint&ri=dval;是正确的,编译器将其转换为:doubledval=1024;inttemp=dval;constint&ri=temp;因有临时对象,引用和类型转换都实现了。当const引用作为形参时,实参也能使用不可寻址的值,并能进行类型转换。运算符的重载2.当用类的成员函数实现运算符的重载时,运算符重载函数的参数(当为双目运算符时)为一个或(当为单目运算符时)没有。运算符的左操作数一定是对象,因为重载的运算符是该对象的成员函数,而右操作数是该函数的参数。3.单目运算符“++”和“--”存在前置与后置问题。前置“++”格式为:返回类型类名::operator++(){……}而后置“++”格式为:返回类型类名::operator++(int){……}后置“++”中的参数int仅用作区分。小结:1.运算符重载函数的函数名必须为关键字operator加一个合法的运算符。在调用该函数时,将右操作数作为函数的实参。运算符的重载运算符运算符名称禁止重载的理由?:三目条件运算符C++中没有定义三目运算符的语法.和.*成员与成员指针操作符为保证成员操作符对成员访问的安全性::作用域操作符该操作符右操作数不是表达式sizeof类型字长操作符该操作符的操作数为类型名,不是表达式表4.1C++中不允许重载的运算符4.C++中只有极少数的运算符不允许重载。运算符的重载问题:例5.7中:c=c+d;语句,改为c=d+c;因为d不是Complex的对象,C++编译器将无法找到合适的重载的“+”运算符对应的函数,最终给出出错信息。怎样解决?友元在C++中友元(friend)函数允许在类外访问该类中的任何成员,就象成员函数一样。友元函数用关键字friend说明。上节答案:用友元函数重载运算符“+”,可以实现c=d+c;友元classComplex{……friendComplexoperator+(double,Complex);};//opration+说明为类Complex类的友元函数Complexoperator+(doubled,Complexc){returnComplex(d+c.Real,c.Image);}//friend只用于类说明中,定义时不加friend//注意友元不是成员函数,但可以直接访问私有成员intmain(void){……c=d+c1;c.print();return0;}解释:d+c被C++编译器解释为:operator+(d,c)友元友元函数重载运算符形式:+有三种形式。另两个的声明为:friendComplexoperator+(Complex,Complex);friendComplexoperator+(Complex,double);涵盖实数与复数,复数与复数,复数与实数相加三种情况。可以仅使用友元函数friendcomplexoperator+(complex,complex);实数被默认的构造函数强制转换为虚部为零的复数。友元比较:友元函数可