版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
5.1概念
5.2重载函数作为成员函数的情况5.3重载函数作为友元函数的情况5.4重载赋值运算符
5.5拷贝构造函数
5.6类型的转换
本章要点
练习
5.1.1运算符的重载
运算符的重载是通过编写函数来实现的。函数名必须由关键字operator和紧跟其后的被重载的运算符组成。例如,若要重载“+”运算符,则重载函数名为operator+;要重载“[]”运算符,则重载函数名为operator[]。
有了函数名,要编写一个函数来实现我们要求的运算,还需要有函数类型和函数参数。运算符有双目运算符和单目运算符。根据重载函数的性质来分,有重载函数是成员函数和重载函数是友元函数两类。根据被重载的运算符来分,有双目运算符重载和单目运算符重载。5.1概念5.1.2运算符重载的限制
C++中提供的运算符重载机制,使程序更清晰,提高了可读性。但C++对运算符重载作了一些限制。
(1)在运算符重载时不能改变运算符原来的优先级、结合性和操作个数等性质。
(2)运算符=、()、[]和->的重载函数,可作为类的成员函数,但不可作为友元函数。
(3)运算符重载函数的参数不能为空。
(4)当参加运算的对象都属内部类型时不能重载。
(5)对可重载的运算符作了限制,如表5-1所示。表5-1运算符表5.2.1双目运算符的重载
双目运算符也称二元运算符,如+、-等。当重载函数作为类的成员函数时,该函数可以少用一个参数,因为成员函数隐藏了第一个参数——this指针。利用this指针就可完成当前对象与重载函数中给出的对象之间的操作。5.2重载函数作为成员函数的情况重载“+”运算符的方法是:Typeoperator-(Types),意为当出现当前类的对象与函数参数给出的对象进行“-”运算时就调用该函数。返回类型对该函数是否被调用没有影响,理论上返回类型可以是任何类型,但一般总是设定为操作对象的类型。
对于上面的operator-函数调用,可理解为程序中有d1-d2的操作,“-”的左边是当前类的对象,右边是type的对象,于是条件满足,就调用该函数。实际上这是在编译时就决定了的,编译器读到d1-d2就将它理解为:this.operator-(d2)。
双目运算符-的左边是当前对象,右边是函数参数给出的对象。所作的运算是,当前对象“-”函数参数给出的对象。因此要注意先后次序。
【例5-1】将两个人民币对象相减,再赋给另一人民币对象。由于相减之后还要赋值,因此要求重载函数返回值。
#include<iostream>
usingnamespacestd;
classRMB
{public:voidmain()
{RMBa(2,50);RMBb(1,60);RMBc;
c=a-b; //将a减去b然后再赋给c
//c=a.operator-(b); //显式调用,与上句等价
c.Display();a.Display();b.Display();
}
运行结果:
0.9
2.5
1.6从函数RMBoperator-(RMBs)的参数中注意到,双目运算符重载函数是成员函数时,参数只用一个,因为operator-是成员函数,它有指向当前对象的this指针。本例中,也可以将operator-的参数改为引用(RMB&s)。
【例5-2】也可以将例5-1中的operator-函数改为如下形式,其他都不变。RMBRMB::operator-(RMBs) //在其中完成圆值和角分
值的分别相减
{jf-=s.jf;
//注意:改变了当前对象的值
yuan-=s.yuan;
while(jf<0){yuan--;jf+=100;}
return(*this);
}
运行结果:
0.9
0.9
1.6在上面的operator-函数中,实现了当前对象与s对象的相减,结果放在当前对象中,返回当前对象的值。该方法改变了当前对象的值,在执行了c=a-b后,a的值也改变了。
我们也可以编写一个成员函数来实现上述功能。例如:将声明中的RMBoperator-(RMB);改为RMBSub(RMB);,将实现中的RMBRMB::operator-(RMBs)改为RMBRMB::Sub(RMBs),再将c=a-b;改为c=a.Sub(b);便可。比较运算符重载和函数调用可以发现,用运算符重载函数直观方便,可读性好。5.2.2单目运算符的重载
单目运算符也称一元运算符,如++、--等。由于它们还有先后之分,因此重载时也分为重载先增量(减量)运算符和重载后增量(减量)运算符函数。若出现同样的operator++或operator--函数名,编译器就无法区分“先”和“后”操作函数。
C++中用函数参数来区分“先”和“后”操作函数。在C++中专门为“后”操作函数提供一个参数int,只给出类型没有变量,别无它意,只为区分“后”函数。
重载先增量运算符的方法是:Typeoperator++(),意为一旦发生当前类的对象进行先增量运算就调用该函数。
重载后增量运算符的方法是:Typeoperator++(int),意为使用当前类的对象进行后增量运算时就调用该函数。
【例5-3】演示先增量和后增量运算符重载函数作为类的成员函数。运行结果:
50
52
【例5-4】若要实现增量之后再赋值,如d1=++d2,那么重载运算符函数就要有返回值。于是将例5-3修改如下:
#include<iostream>
usingnamespacestd;
classRMB运行结果:
51
51
52
先增量函数是先增值后返回,后增量函数也增值,但是它所要得到的是改变之前的值,所以要申请一个临时对象暂存原值,然后再返回该临时对象。5.3.1双目运算符的重载
要通过重载的运算符函数对类进行操作,访问类的保护成员或私有成员,必须将其声明为类的友元。
双目运算符的操作数既可以是两个不同类型的对象(如对象与内部类型变量或常数),也可以是两个相同类型的对象。
【例5-5】人民币有圆、角和分。要求实现人民币对象的加操作,使其自动实现正确的操作。5.3重载函数作为友元函数的情况voidmain()
{RMBd1(4,50);RMBd2(6,68);RMBd3(0,0);
d3=d1+d2; //调用operator+(RMB&s1,RMB&s2)
d3.Display();
}
运行结果:
¥11.18注意operator+(RMB&s1,RMB&s2)函数的调用时机,当程序中有“d1+d2;”的操作且在“+”的左边和右边都是RMB对象时,条件满足,就调用该函数。实际上这是在编译时就决定了的,编译器读到“d1+d2;”就将它理解为operator+(d1,d2)。
在operator+函数中完成对应的圆加圆,角分加角分,然后以新得到的圆和角分值构造一个新的对象,在构造函数中进行修正。在本例中要实现d3=d1+d2,于是就将operator+函数的类型声明为RMB。一般来说,把函数的参数设置为同类对象的引用,还可以提高执行速度。如friendRMBoperator+(RMB&,RMB&);就可以发挥出引用的长处,而且这是常用方法。
若感到运算符的重载不够清晰,可用函数调用的方法实现两个用户自定义对象的操作。我们还举上面“+”的例子。例如,在类中声明friendTypeAdd(Type1a,Type2b);,在类的实现中完成函数体函数定义,在程序中用Add(a,b);调用,就可以完成同样的功能。
【例5-6】通过添加友元函数Add完成两个RMB对象的相加。运行结果:
¥11.18
由例5-6得到了与例5-5同样的结果,但显然这两种方法的效果不同。用函数就需要人为地调用语句,用重载运算符就不同了,当条件满足时能自动调用重载函数。重载运算符的可读性好,直观方便。
【例5-7】通过完成两个复数的加和减操作,演示如何重载“+=”运算符。运行结果:
a+b=4+6i
a=3+4i
b-a=-2-2i
b=1+2i
(b+=a)=1+2i
对重载运算符来说,若重载了“+”和“=”号并不等于有了“+=”的重载,因重载运算符并不具有结合性。在本例的重载运算符函数是传值调用,是在副本上进行的操作,因此建立了一个临时对象temp暂存操作结果,然后再将temp返回。正因为是返回对象,因此可用(a+b).desplay();等来输出结果。
【例5-8】重载流插入<<和流提取>>运算符,完成输入电话号码然后再输出的功能。运行结果:
Enteraphonenumberinthefrom456_87890649
0512_99208518↙
Thephonenumberenteredwas:
(0512)-992085185.3.2单目运算符的重载
对于单目运算符的重载,C++规定,若为先操作则返回引用,若为后操作则返回值。这是因为,在后操作中既要改变对象值,又要得到改变前的对象值,于是便生成一个新对象来保存操作前的对象,所以返回值;在先操作中直接作用于原对象本身,并不需要生成一个新对象来存放结果,所以返回引用。先后操作的意义在于决定了其不同的返回方式。
对于函数,参数要求设置为引用,因为增减操作往往作用于原对象,而不是它的副本。
【例5-9】演示++运算符的重载。运行结果为:
thevalueis20
thevalueis21
thevalueis22
thevalueis23
thevalueis23
thevalueis23
thevalueis24本例中的语句friendIncreaseoperator++(Increase&,int);中的int仅作为后操作的标识。在++运算符重载函数的声明中先加返回引用,后加返回值。由于(n++).Display();是后加,因此匹配的是Increaseoperator++(Increase&a,int)函数。该函数用当前对象构造了一个临时对象temp,然后将当前对象增值,最后返回临时对象temp,因此输出仍为23。接着n.Display();输出24。系统对C++中的赋值运算符已有了默认的操作方式,如d=3;是赋值运算符的应用。若有两个对象a、b也进行a=b;操作,结果如何呢?看下面的例子。
【例5-10】现仍以复数操作为例,直接通过“=”来相互赋值。
#include<iostream>
usingnamespacestd;
classComplex
{public:5.4重载赋值运算符运行结果:
b=3+4i
在程序中并没有重载“=”运算符,但它也实现了“赋值”,而且能自动完成实部和虚部分别“赋值”。C++的编译器对于这种情况,首先检查有无重载的赋值运算符函数,若无则调用系统提供的默认拷贝构造函数,进行简单(浅)拷贝。一般情况下赋值运算符不需要重载,但是下面演示的情况却有所不同。
【例5-11】分析用赋值运算符对具有申请资源的对象赋值时存在的缺陷。运算结果:出现错误。
出现错误的原因是两个对象的数据成员str指向了同一个内存地址。赋值运算只复制指针,不复制指针所指向单元内的值(资源),结果两个指针指向了同一个内存地址。执行程序结束时自动调用析构函数,释放了通过new分配的数据成员str指向的内存,当第二个对象调用析构函数时试图再次释放同一内存,于是就发生了错误。解决问题的方法是重载赋值运算符。重载赋值运算符的目的是完成资源的复制或拷贝。
【例5-12】通过重载赋值运算符,演示如何实现自定义类的赋值。对于上面出现的s2=s1,编译器在工作时会检查每个非静态数据成员。如果它们都属内部类型(即基本类型或非用户自定义的类型),则右边的实例赋给左边。如果是自定义类型对象,并且该类定义了重载赋值运算符函数,则自动调用该函数,否则调用默认拷贝构造函数进行按成员赋值。5.5.1拷贝的需要性
对象的拷贝分为浅拷贝和深拷贝两种。假定对象s拥有一个资源,用s的值创建一个t对象。如果t仅仅是在二进制内存空间上对s的拷贝,那就意味着t也拥有这个资源了。那么该资源属于s,还是属于t,资源归属权不清,将引起资源管理混乱。这种只复制成员(如指针指向)不复制资源的情况,称为浅拷贝,见图5-1。对于既复制成员又复制资源的情况,称为深拷贝,见图5-2。5.5拷贝构造函数图5-1浅拷贝图5-2深拷贝所有类都有一个默认的拷贝函数,它是在编译时由系统自动提供的函数。默认拷贝函数所做的工作是完成浅拷贝。若要实现深拷贝,必须再提供拷贝函数,称为拷贝构造函数。
当我们在堆内存中分配了资源后,就要提供一个拷贝构造函数;当某类的对象需要使用硬件设备时,就要提供一个拷贝构造函数。
需要说明的是,对于运算符“=”,我们也能达到“拷贝”的要求。它有两种方法:一种是按默认方法进行简单赋值,即浅拷贝;另一种是按用户要求进行资源拷贝,即深拷贝。深拷贝时必须重载“=”运算符。5.5.2拷贝的发生
拷贝构造函数首先是构造函数,是构造函数就不可人为调用,而是由系统自动调用的。在下列三种情况下,将会发生调用拷贝构造函数。
1.用对象构造对象
当用一个对象去构造另一个对象,即用一个对象初始化一个新对象时,将调用拷贝构造函数。例如:
Students(“Jenny”); //调用构造函数
Studentt=s; //调用拷贝构造函数
或者
Students(“Jenny”);
Studentt(s); //用s对象去构造t对象
这两种写法的作用是一样的,但是要注意,若用Students("Jenny");Studentt=s;,则它是赋值而不是初始化,它并不会调用用户自定义拷贝构造函数。它首先检查有无重载的“=”运算符函数,若无则调用系统默认的拷贝构造函数,完成浅拷贝。若s不是Student类的对象,则要进行转换,这将在下一节中再介绍。
2.对象作为函数参数
当对象作为函数参数传递时,将调用拷贝构造函数。例如,在下面的代码中,形参s是t的一个拷贝。
Studentfun(Students)//普通,调用它是实参拷贝给形参
{Studenttemp;
…
returntemp;
//返回对象,发生拷贝
3.函数返回一个对象
当一个函数返回一个对象时,将调用拷贝构造函数,进行对象的拷贝。
【例5-13】虽然不能用临时对象来初始化引用,却可以将临时对象拷贝给一个同类对象。
#include<iostream>
#include<string>
usingnamespacestd;
classPerson
{protected:运行结果:
Construct Kate //构造wom
Construct Alice
//构造temp
DestructingAlice //析构temp,它是局部对象
DestructingAlice //析构临时对象
mainend
DestructingAlice //在主函数结束后析构wom析构temp时,temp返回temp拷贝的临时对象;在主函数中完成wom=Set()拷贝后,该临时对象析构。创建临时对象时调用的是拷贝构造函数。由于本例没有提供拷贝构造函数,因此进行的是浅拷贝。所以在本例的输出中发生了构造与析构不是成对出现的现象。若该对象在堆中分配了空间,再用此方法就会出错。
【例5-14】演示当对象在堆中申请分配一个资源时,浅拷贝将出错。5.5.3实现拷贝的方法
浅拷贝将会出现只复制指针不复制指针指向单元内的值或对空指针赋值的问题。要想实现深拷贝,有两种方法:方法一是重载赋值运算符;方法二是自定义拷贝构造函数。重载赋值运算符前面已作了介绍,在此主要介绍自定义拷贝构造函数。
定义拷贝构造函数的一般格式为:
类名::函数名(类名&对象名)例如:Student::Student(Student&s)拷贝构造函数名与类同名,参数表中是本对象的引用。由于参数的不同,构造函数也不同。
因为自定义拷贝构造函数是在用拷贝的方式创建对象,所以必须有一个对象作为实参。又因为它是构造函数,在它执行时对象还未创建,还是一片空白,所以它的参数只能使用引用的方式,如Student(Student&s)。若用拷贝的方式,如Student(Students),就会使任何调用都陷入无限的递归中。5.5.4拷贝构造函数的使用
对于拷贝构造函数,首先考虑的问题是是否需要。作为一般的经验,若类中有指针作为数据成员,就有可能需要拷贝构造函数,或者说当需要析构函数来释放资源时,就需要拷贝构造函数。
【例5-15】演示拷贝构造函数在对象复制中的应用,将例5-11用拷贝构造函数的方法实现。运行结果:
Welcometomyworld
Welcometomyworld
通过上例实现了在拷贝数据的同时拷贝资源的问题。对于上面出现的Strings2=s1,编译器在工作时,按声明的顺序检查每个非静态数据成员。如果它们是非类类型,则右边的实例被拷贝到左边。如果是类类型,并且该类定义了拷贝构造函数,则自动调用该函数,否则调用默认拷贝构造函数进行按成员赋值。默认拷贝构造函数与默认构造函数不同。默认构造函数是空函数,什么也不做,一旦提供了构造函数,原来的默认构造函数就消失。默认拷贝构造函数执行浅拷贝,当程序员提供了拷贝构造函数之后,默认拷贝函数仍存在,并还在发挥作用。当然对于上面的例题,若不在堆中申请空间,用类库中的string类实现就很方便。
【例5-16】演示类库中的string类的使用。
#include<iostream>
#include<string>
usingnamespacestd;
classString5.6.1系统的自动转换
对于基本的数据类型,当两个不同类型的数据相互运算时,系统自动向高类型数据转换。例如:
#include<iostream>
usingnamespacestd;
voidmain()
{cout<<8/5<<'\t'<<8.0/5<<endl;}5.6类 型 的 转 换运行结果:
1 1.6
其中,1是int,1.6是double。
以上的操作数是基本类型,由系统自动进行转换,称为隐式转换。也可以进行强制转换,称为显式转换。例如:
#include<iostream>
usingnamespacestd;
voidmain()
{cout<<(double)8/5<<'\t'<<8.0/5<<endl;}运行结果:
1.6 1.6
由于自定义类型对编译器来说是不可知的,程序员对类型的定义只是告诉编译器有了这些新类型,因此若要将基本类型转换为一个自定义类型,转换方法必须由用户告知。告知方法就是提供一个用于转换的构造函数,也可以编一个转换函数。5.6.2转换构造函数
通过使用构造函数,既可以将基本类型转换为用户自定义类型,也可以将用户自定义类型转换为基本类型。当构造函数的功能不作初始化而用于类型转换时,我们称该构造函数为转换构造函数。
实现转换的方法是:定义含有一个参数的构造函数,并在函数体中完成所需要的类型转换。
【例 5-17】将实型数据中以米为单位的值,转换为以英尺和英寸为单位的值。
#include<iostream>
usingnamespacestd;
classEnglish
{private:
intfeet;floatinches;voidmain()
{Englisheng; //调用第一个构造函数
eng=8.0f; //调用转换构造函数
eng.Display();
EnglishengA(8); //调用第三个构造函数
engA.Display();
EnglishengB(26,2.88f); //调用第二个构造函数
engB.Display();
}运行结果:
feet:26inches:2.88
feet:26inches:2.88
feet:26inches:2.88
本例通过一个用于转换的构造函数实现了公制转换英制。执行eng=8.0f;就调用了转换构造函数English(floatmetres),将公制的数据8.0转换为英制的对象eng。因为eng=8.0f是赋值运算,所以编译器首先检查是否有重载的赋值运算符,若无,则使用构造函数进行转换。若无构造函数就报错。比较一下eng=8.0f与Convertereng(26,2.88f)的区别,不难发现,虽然是同样的结果,但是使用的条件和概念是不同的。用eng=8的方法是转换,用Convertereng(26,2.88f)的方法是构造。用于类型转换的构造函数有且仅有一个参数,而构造函数可以有多个参数。
添加一个实现转换功能的成员函数也能实现上述要求。用了转换构造函数,就可用“=”运算符来达到转换的目的,既方便,又提高可读性。5.6.3转换函数
除了构造函数可以实现类型转换外,转换函数也能实现类型转换。转换函数是一种特殊类型的成员函数。它定义了一个由用户定义的转换,以便把一个类对象转换为基本类型或其他类型。
转换函数的原型为:operator目标类型();,它的返回类型就是目标类型。即在类体中通过指定关键字operator,并在其后加上转换后的目标类型,就声明了转换函数。例如,使用Converter::operatorfloat()就能将Converter类的对象转换为实型数据。转换函数既可以将当前对象转换为基本型对象,也可以将当前对象转换为其他类型对象。例如Converter::operatorOtherclass()。
【例5-18】公英制数据的互换。
#include<iostream>
usingnamespacestd;
classConverter
{private:
intfeet;floatinches;intmetre;public:
Converter(){feet=0;inches=0.0;}
Converter(float); //转换构造函数
operatorfloat() //转换函数,将对象转换为实型
{floaty;y=inches/12;y+=float(feet);return(y/3.28f);}
voidDisplay()
{cout<<feet<<“and”<<inches<<endl;}
//输出英尺和英寸
};Converter::Converter(floatmetres)
//基本型→自定义类型
{floaty;y=3.28f*metres;
feet=int(y);
inches=12*(y-feet);
}运行结果:
27and10.56
27and10.56
8.5
对象eng的定义调用了第二个构造函数,在此它将实型数8.5转换为类的Converter对象。若用ConverterEng(8.5f);,则也是调用第二个构造函数,两者在此是等价的。转换和构造,主要看函数的功能。对于operatorfloat(){}函数,原来介绍的是返回类型operator运算符(),现在运算符被类型float取代,此时意为对实型数据的运算重载,运算结果和返回类型都应当是实型,所以不需要也不能再指定返回类型。当出现“=”运算右边是float型、左边是Converter型的对象时,自动调用该转换函数。
【例5-19】将类对象转换为字符串,即将自定义类型转换为基本类型。
#include<iostream>
#include<string>
usingnamespacestd;
constintSIZE=60;
classString
{private:
charstr[SIZE];运行结果:
Enterastring:china↙
Stringinobject=china
Stringp=china
Stringinobject=NewString
运行结果分析:在匹配时,因为p=obj结果为字符指针,它将用户自定义类型转换为基本类型,所以匹配operatorchar*();函数。其中,char*是函数的返回类型。因为obj=s结果为对象,先查找有无重载的赋值运算,没有则调用构造函数,所以匹配String(char*s);函数。
对于将基本类型转换为对象,一般用构造函数作为转换函数来实现;对于将对象转换为基本类型,一般通过指定关键字operator,并在其后加上转换的目标类型来声明转换函数。5.6.4转换函数的匹配方式
上面已经介绍了用转换函数和用构造函数实现类型转换。从结果来看,它们的共同特点是,编译器都能自动地调用最佳匹配的函数。这种自动调用方式正是转换函数的一个奇妙特征之一,它给我们带来了方便。如在例5-19中的p=obj和obj=s,编译器分别自动匹配operatorchar*()和String(char*s)。那么在既有转换又有赋值时,编译器将如何匹配呢?
匹配的依据,根据操作性质和参数的类型而定。若给出一种操作(转换),找不到与之匹配的函数,就出错。
【例5-20】演示各个函数的匹配方式与次序。
#include<iostream>
usingnamespacestd;
classTest
{public:
Test(){cout<<"Thefirstfunction"<<endl;a=0;b=0;}Test(floatx){cout<<"Thesecondfunction"<<endl;a=x;b=8.8f;}
voidoperator=(Test&s){cout<<"Thethirdfunction"<<endl;a=s.a;b=s.b;}
Test(Test&p){cout<<"Thefourthfunction"<<endl;a=p.a;b=p.b;}
operatorfloat(){cout<<"Thefifthfunction"<<endl;returna+b;}
operatorint(){cout<<"Thesixthfunction"<<endl;;returnint(a)+int(b);}
voidDisplay(){cout<<a<<'\t'<<b<<endl;}d1=d2; //它是赋值运算,调用第三个函数
cout<<“d1:”;d1.Display();
Testd3(d2);
//调用第四个构造函数
cout<<"d3:";d3.Display();
m=d2; //调用第五个函数
cout<<"m:"<<m<<endl;
n=d2; //调用第六个函数
cout<<"n:"<<n<<endl;
}运行结果:
Thefirstfunction
d1: 00
Thesecondfunction
d2:1.55 8.8
Thethirdfunction
d1:1.55 8.8
Thefourthfunction
d3:1.55 8.8
Thefifthfunction
m:10.35
Thesixthfunction
n:95.6.5转换函数的作用
通过前面的几个例子不难得出这样的结论,要想实现不同类型的转换,可以用转换构造函数,也可以用转换函数。那么为什么要引入转换函数呢?以下说明原因。若要对两个不同类的对象进行多种操作,或对象与内部类型数据进行多种操作,比如我们要使String的对象与int数进行“+”操作,就用:
classString
{friendoperator+(String&,int);
friendoperator+(int,String&);
public:
String(inta){value=a;}
};
String对象与整型参数就需要两个重载函数,若还要做“-”操作,则需要的重载函数将翻倍,更不用说还有“*”、“/”、位运算、逻辑和关系运算等,若再增加参数个数,就又有多种组合。因此C++中提供了转换函数,通过它,每个类都可以定义一组“可被应用在该类型对象上的转换”,从而将对象转换成我们所需要的内部类型。
【例5-21】演示转换函数的作用。
#include<iostream>
usingnamespacestd;
classTest
{private:
intvalue; doublescore;
public:
Test(inta,doublex)
{value=a;score=x;}
operatordouble(){returnscore;}
//只用一个函数就可完成8种运算
};voidmain()
{Testa(34,7.8);intx;doubley;
x=a;y=a; //先用转换函数,转换为基本类型
cout<<x+3.14<<endl;
//然后就可以进行基本类型数据运算
cout<<3.14+x<<endl;
cout<<3.14-y<<endl;
cout<<3.14*y<<endl;
cout<<3.14/x<<endl;
cout<<5+x<<endl;
}从上面的程序可见,有了转换函数,我们就可以先将自定义类型转换为基本类型,以后所做的操作实际上就是两个基本类型数据的操作。例如,当进行x=a或y=a时,编译器隐式地调用转换函数Test::operatordouble(),将对象a转换为double类型,于是就可以做常规的运算了。其实质就是先转换后操作,从而解决复杂的重载运算符操作问题。● 如果运算符重载比用明确的函数调用使程序更清晰,那么就使用运算符重载。运算符重载的目的是使用户自定义类型也能使用运算符操作,从而提高程序的可读性。
● 运算符的重载是通过编写函数定义来实现的。函数名必须由关键字operator和其后要重载的运算符组成。
● 在运算符重载时不要改变运算符原来的优先级、结合性和操作个数等性质。
● 运算符重载提高了C++的可扩展性。本章要点● 运算符的重载函数可以是类的成员函数,也可以是类的友元。对于运算符=、()、[]和->的重载函数,可作为类的成员函数,但不可作为友元函数。
● 若没有定义拷贝构造函数,那么在编译时系统将自动提供一个默认的拷贝构造函数,完成浅拷贝。
● 默认拷贝构造函数与默认构造函数不同,默认构造函数是空函数。
● 若在堆内存中分配了资源,就要提供一个拷贝构造函数,实现深拷贝。● 拷贝构造函数由系统自动调用,在三种情况下会发生调用拷贝构造函数。
● 转换函数通过定义的函数来完成不同类型对象之间的转换。
● 用于类型转换的构造函数最多只能含有一个参数。
● 转换函数并不一定要由重载构造函数或用operator关键字来完成,一般函数也能实现转换。两者的区别是:用构造函数可以在出现“=”运算符时由系统自动调用;用一般函数则需要显式调用。
● 选用何种方式转换,要由语义是否清晰来决定。一、概念
1.运算符重载本质上是通过
来实现操作要求的。
2.运算符重载的目的是:
。
3.双目运算符的重载函数作为类的友元函数时,对函数中参数的次序是
要求的。
4.双目运算符的重载函数作为类的成员函数时,它有
参数,此时可以解释为
,自动调用该函数。练习
5.在重载前后增(减)量操作符时,C++中规定,前增(减)量返回
,后增(减)量返回
。
6.在
的情况下程序员要提供拷贝构造函数。
7.拷贝构造函数参数的特点是:
。转换函数的特点是:
。二、分析
1.指出下面程序的运行结果。
#include<iostream>
usingnamespacestd;
classRMB
{public:
RMB(){yuan=0;}
RMB(intd){yuan=d;} RMB&operator++();
RMBoperator++(int);
voidDisplay()
{cout<<yuan<<endl;}
protected:
intyuan;
};
RMB&RMB::operator++()
{++yuan;
return*this;
}RMBRMB::operator++(int)
{RMBtemp(*this);
yuan++;returntemp;
}
voidmain()
{RMBd1(50),d2(50);
(++d1).Display();
(d2++).Display();
++++++d1;
(d2++);
d1.Display();
d2.Display();
}2.指出下面程序的运行结果。
#include<iostream>
usingnamespacestd;
classIncrease
{public:
Increase(intx,floaty)
{value=x;val=y;}
Increase&operator++();
Increaseoperator++(int);
voidDisplay()
{cout<<"thevalueis"<<value;cout<<"thevalis"<<val<<endl;}protected:
intvalue;floatval;
};
Increase&Increase::operator++()
{value++;val++;
return*this;
}
IncreaseIncrease::operator++(int)
{Increasetemp(*this);
value++;val++;
returntemp;
}voidmain()
{Increasen(20,3.1f);
n.Display();
(n++).Display();
n.Display();
++n;
n.Display();
++(++n);
n.Display();
n++;
n.Display();
}3.指出下面程序的运行结果。
#include<iostream>
usingnamespacestd;
classPerson
{public:
Person(char*pN="noname");
Person(Person&p);
~Person();
private:
char*pName;
};Person::Person(char*pN)
{cout<<"ConstructingPerson"<<pN<<endl;
pName=newchar[strlen(pN)+1];
if(pName!=0)
{strcpy(pName,pN);}
}
Person::Person(Person&p)
{cout<<"Copting"<<p.pName<<"intoitsownblock\n";
pName=newchar[strlen(p.pName)+1];
if(pName!=0)strcpy(pName,p.pName);
}Person::~Person()
{cout<<"Destructing"<<pName<<endl;deletepName;}
Personfun(Persona)
{Personms(a);
returnms;
}
voidmain()
{Personp("Amadis");
Persons=fun(p);
Personms("Kansas");
Personns=ms;
}
4.改正下面程序中的错误。要求不用string类库,实现两个字符串的连接。
#include<iostream>
usingnamespacestd;
classString
{private:
charstr[80];
public:
String(){strcpy(str,“”);}
String(char*p)
{strcpy(str,p);}voidoperator+=(Strings)
{if(strlen(str)+strlen(s.str)<80)
{strcat(str,s.str);return(*this); }
else
{cout<<"stringistoolong";
return(*this);
}
}
voidDisplay(){cout<<str<<endl;}
};voidmain()
{Strings1="IuseC++"; Strings2="testtheconnect";
s1+=s2;
s1.Display();
Strings3;
s3=s2;
s3.Display();
}
5.下面的程序若第一次输入:95,第二次输入:59,请分析两次运行的结果。
#include<iostream>
usingnamespacestd;
classArray
{protected:
intsize;int*p;
public:
Array(int=3);
~Array(){delete[]p;}
int&operator[](int);
};Array::Array(intn)
{size=n;p=newint[size];for(inti=0;i<size;i++)p[i]=0;}
int&Array::operator[](intx)
{if
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 配电网负荷动态平衡技术
- 保险行业数字化转型模板
- 职业导论-2018-2019年房地产经纪人《职业导论》真题汇编
- 房地产交易制度政策-《房地产基本制度与政策》真题汇编4
- 要怎么写问卷调查报告
- 人教版三年级数学下册第三单元复式统计表综合卷(含答案)
- 山西省朔州市部分学校2024-2025学年八年级上学期期末生物学试卷(含答案)
- 产权技术合同在跨国技术转移中的法律风险与防范
- 苏州中考英语模拟试卷单选题及答案
- 二零二五版房屋遗产继承分配与拆除重建工程融资合同3篇
- 成品可靠性测试计划
- 汉声数学绘本《数是怎么来的》
- 人工智能基础与应用课件
- 仿制药一致性评价
- 【译林版】六年级英语(下册)单词默写单
- 计算机二级wps题库及答案
- 整套课件:工业催化
- 爆破安全管理知识培训
- 旅游地理学教案
- 煤矸石综合利用途径课件
- 企业信息公示联络员备案申请表
评论
0/150
提交评论