有关运算符重载的归纳
1.运算符的重载体现了面向对象的程序设计的灵活性与可适用性,能够结合实际问题设计并方便问题的解决。例如设计的复数类,对于其定义的类的对象的操作可以通过运算符快速且便捷的实现,同时也可以体现类的封装性,也方便使用者能够更好的理解代码。 运算符的重载使类的设计丰富多样,扩大了类的功能和适用范围,使程序易于理解,易于对对象进行操作,它体现了为用户着想、方便用户使用的思想。有了好的类,使用者就不必再利用复杂的函数调用来实现,能够使主程序更加简单易读。好的运算符重载能够体现面向对象程序设计的思想。
2.使用运算符重载的具体做法:
(1)先确定要重载的是哪儿一个运算符,想把它用于哪儿一个类。重载运算符只能够将一个运算符用于一个指定的类而不是所有的类。
(2)设计运算符重载函数和有关的类。
(3)在实际的工作之中,很多情况下并不是我们自己去编写一个类以及其中的成员函数(包括运算符重载函数等),而是这些工作由其他人完成,我们仅仅需要调用和使用即可,这叫做项目的合作。在使用其他人定义的类时,我们需要包含类的设计者所提供的头文件,这样就能够在编码过程当中使用其他人的类。
(4)使用者需要了解在该头文件包含哪儿些运算符的重载,适用于哪儿些类,有哪儿些参数。也就是需要了解运算符的重载函数的原型,就可以方便的使用该运算符了。
(5)如果有特殊的需要,并且没有现成的重载运算符可以使用,就需要自己设计重载运算符函数。应当注意把每次设计的重载运算符函数保存下来,以免下次用到时要重新设计。
3.在本章所举的各个例子当中,我们发现函数不采用以往的虚实结合的方式,而是采取引用的方式,使用这种方式我们可以减少时间和空间的开销。如果重载函数的返回值是对象的引用时,返回的不是常量,而是引用所代表的对象,它可以出现在赋值号的左侧而成为左值,可以被赋值或参与其他操作。但使用引用时要小心,因为修改了引用就代表修改了它所代表的对象。
4.c++大多数运算符都可以重载,在本章所举的例子当中仅仅牵涉到了部分运算符的重载,希望大家可以通过这些例子举一反三。
不同类型数据间的转换
标准类型数据间的转换
隐式转换如下例,属于系统的自我转换
i = 6;
i = 0.5 + i;
第二行代码运行时i的值为0.5+6=6.5,但是由于i时整形数据,所以自动转换之后i值为6
c++也提供了显示转换,其形式为类型名(数据) ,在c语言当中的形式为(类型名)数据
系统可以了解标砖类型数据之间的转换,那么我们是否可以类比一下,将一个类的对象转换为另一个类的对象呢?答案是可以的,但是我们要用某种方式让系统知道,我们怎么引导它实现类型的转换。
用转换构造函数进行不同类型数据的转换
转换构造函数的作用是将一个其他类型的数据转换成一个类的对象
那么为了避免混淆其他构造函数的概念,我们先来复习一下构造函数的几种类型:
1.默认构造函数
Complex();
2.用于初始化的构造函数
Complex(double a,double b);
3.用于复制对象的复制构造函数
Complex(Complex &c);
4.转换构造函数
Complex(double r){real=r;imag=0;}
那么上例当中的这个转换构造函数的功能是将一个浮点型转换为复数类的对象,并将r作为复数的实部,虚部为0.用户可以根据需要定义转换构造函数,在函数体中告诉编译系统怎么样去类型转换。在类体当中可以有转换构造函数,也可以没有转换构造函数,视需要而定。
以上的四种构造函数可以同时出现一个类当中,他们是构造函数的重载。编译系统会根据建立对象是给出的实参的个数与类型选择形参与之匹配的构造函数。
Complex c(3.5);
调用转换构造函数使浮点型常数3.5转换为一个名为c的实部为3.5虚部为0的复数类的对象
当然也可以建立一个无名对象,但无法使用
Complex(3.5);
不过可以创建一个类的有名对象,将无名对象赋值给有名对象
c=Complex(3.5);
由于运算符重载的定义使重载过后的运算符的运算两侧必须要是同类型
所以当用一个复数的对象与一个浮点数相加时编译会出错,这时候就可以对于浮点数进行类型转换
c=c1+Complex(3.5);
类的类型转换于标准类型数据间的转换相同,也存在强制类型转换,如上例。
转换构造函数的定义要有意义,转换构造函数只能有一个参数,如果有多个参数,则不为转换构造函数,因为系统无法确定将哪儿个参数转换为类的对象。
使用转换构造函数将一个指定的数据转换为类对象的方法如下:
1.先声明一个类。
2.在这个类中定义一个只有一个参数的构造函数,参数的类型是需要转换的类型,在函数体中指定转换的方法。
3.在该类的作用域内可以用以下形式进行转换:类名(指定类型的数据) ,就可以将指定类型的数据转换为此类的对象。
当然对于转换构造函数的使用不仅仅可以将一个标准类型数据转换为类对象,也可以将另一个类的对象转换成转换构造函数所在的类对象,例如:
Teacher(Student& s){num=s.num;strcpy(name,s.name);sex=s.sex;}
学生毕业之后当了教师,数据的转入,但注意对象中的num,name,sex必须是公用成员,否则不能被类外引用。
类型转换函数
前面介绍了构造转换函数能够将一个标准类型的数据转换为一个类的对象、一个类的对象转换为另一个类的对象,那么能不能用一种函数让一个类的对象转换成一个标准类型的数据呢?
c++提供了类型转换函数来解决了这个问题,类型转换函数是将一个类的对象转换成另一类型的数据,比如在一个复数类当中
operator double()
{return real;}
函数返回的是double型real的值,请注意函数名是operator+类型名,即类型转换函数的模板是
operator 类型名()
{实现转换的语句}
在函数名之前不能够指定函数的类型,且函数没有参数,其返回值的类型是由函数名中指定的类型名所确定的。类型转换函数只能够作为成员函数,因为转换的主体是本类的对象,不能作为友元函数或者普通函数。从函数形式上可以看得出来类型转换函数于运算符重载函数较为相似,其类型名的使用不仅可以使系统能够识别原有的double型,还可以将Complex类对象作为double型的数据处理。Complex类对象只有在需要时才转换,并不是所有的类对象都一律转换为double类型数据。
转换构造函数和类型转换函数都有一个共同的功能:在需要时,编译系统会自动调用这些函数,建立一个无名的临时对象或者临时变量。
结合实际来说就是:
转换构造函数是将标准类型数据转换为类对象
类型转换函数是将类对象转换为标准类型数据
所以当在使用加法的时候,一共可以看作有两种情况供我们了解
第一种情况:在一个复数类当中double d1,d2;Complex c1;运行d1=d2+c1;那么此处的加号若未经过重载且不能对于复数类进行加法运算且该类当中含有对于复数类的类型转换函数使复数类型转换为double类型的函数时,系统就会自动寻找我们定义的类型转换函数将加号左右的类型调整为一致,即将c1调整为double类型。
#include <iostream>
using namespace std;
class Complex
{
private:
double real;
double imag;
public:
Complex(){real=0;imag=0;}
Complex(double a,double b){real=a;imag=b;}
operator double(){return real;}//类型转换函数,将复数类对象转换为double类型数据
};
int main()
{
double d1,d2=2.4;
Complex c1(1.2,2.4);
d1=d2+c1;//2.4+1.2=3.6
cout<<d1;
return 0;
}
第二种情况:在一个复数类当中double d1;Complex c1,c2;运行c2=c1+d1;那么此处的加号若经过复数类的重载,能运行复数类对象的加法时,且复数类当中含有构造转换函数时,系统会自动调用构造转换函数使加法两边的数据类型均为复数型,即d1转换为Complex型。
#include <iostream>
using namespace std;
class Complex
{
private:
double real;
double imag;
public:
Complex(){real=0;imag=0;}
Complex(double a,double b){real=a;imag=b;}
Complex(double a){real=a;imag=0;}//转换构造函数
friend Complex operator + (Complex A,Complex B);
void display();
};
int main()
{
Complex c1,c2(3.6,4.8);
double d1=1.2;
c1=c2+d1;//(1.2,0)+(3.6,4.8)=(4.8,4.8)
c1.display();
}
Complex operator + (Complex A,Complex B)
{
return Complex{A.real+B.real,A.imag+B.imag};
}
void Complex::display()
{
cout<<'('<<real;
if(imag>0)cout<<'+'<<imag<<'i'<<')'<<endl;
else cout<<imag<<'i'<<')'<<endl;
}
从其中的隐式调用我们可以发现一个规律,在已定义了相应的转换构造函数的情况下,将运算符'+'重载为友元函数时可以使用交换律。如果运算符重载函数不为友元函数而为类的成员函数时,则结论不成立,因为作为运算符重载函数成员函数时,省略了一个形参,这个形参往往以this指针的形式指向其对象,例如
c1+2.5
系统把它识别成为c1.operator + (2.5);可以正确运行等价于c1.operator + (Complex(2.5));
2.5+c1
系统则会将它识别为2.5.operator + (c1);很显然是错误的
通常把类型转换函数称为类型转换运算符函数,由于它也是重载函数,因此也称为类型转换运算符重载函数。
结论:如果运算符函数重载为成员函数,它的第一个参数必须是本类的对象。当第一个操作数不是类对象时,不能将运算符函数重载为成员函数,如果将运算符'+'+函数重载为类的成员函数,则交换律不适用。
所以在一般情况下将双目运算符重载为友元函数,将单目运算符则多重载为成员函数
如果第一个参数不是本类的对象时一定要将运算符函数重载为成员函数也不是不行,要定义另外一个重载函数,只是形参位置不同,从而实现加法交换律。
同时还有一个小条件要记住:类的转换构造函数、类的运算符重载函数不能够与类的类型转换函数同时存在,否则将会出现二义性,使系统无法识别,造成编译错误。