重载运算符函数引言
前面我们介绍过函数的重载,即同样的函数能够实现不同的功能
其实运算符也可可以重载
在系统中,我们使用预处理时,#include
即>>可以当作流插入运算符使用也可以当作位移运算符使用
且<<可以当作流提取运算符使用也可以当作位移运算符使用
假定我们设置了一个专门的复数类
那么我们是可以通过它的成员函数来实现数据的输入和输出以及加减乘除等的基本运算
例如下例我们用类的成员函数实现复数类的加法
#includeusing 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 Complex_add(Complex &B); void display(); }; int main() { Complex a(1.2,2.4),b(2.4,3.6),c; c=a.Complex_add(b); c.display(); return 0; } void Complex::display() { cout<<'('< 我们利用类的成员函数实现了类对象的加法,在这个成员函数当中,我们发现,复数加法的成员函数的形参仅为一个,在运算加法的过程当中,我们创建了一个临时类对象,其实我们定义的复数加法成员函数是隐藏了一个形参,第一个隐藏的形参为对象本身,所以创建一个临时对象之后,可以将临时对象的实数数据赋值为本对象与传入形参对象的实数的和,虚数的运算也是同样的道理。函数返回临时变量c,即我们在代码中创建了一个新的对象c,这个名字为c的对象接受了临时对象的数据,从而实现了两个复数的相加。
那么运用成员函数实现类的对象的加法运算是可以实现的,但是当代码长度过于长时,代码量会比较大且检查的时候不容易直接找到出错点,有没有一种简便的方法可以避免这种情况呢?那就是运算符的重载。
运算符的重载
运算符重载实质上来说就是函数的重载
对于原有的运算符进行重载,也就是对于定义函数实现,使原有的运算符不仅可以实现原有的功能,还能够实现我们定义的功能。在使用被重载的运算符时,系统自动调用其所在的函数模块,从而实现相应的功能。
运算符的重载在函数的定义上仅仅有很小的改变,就是在函数名上,函数名变成了关键字operator和所重载的运算符组成,即
函数返回类型 operator 所重载的运算符 (形参表){函数内容}
那么根据运算符重载的概念,我们可以对于上次使用成员函数实现复数和的代码进行修改
#includeusing 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 operator + (Complex &B);//将原有的成员函数声明改为运算符的重载函数的声明 void display(); }; int main() { Complex a(1.2,2.4),b(2.4,3.6),c; c = a + b;//在使用时仅仅用我们重载过的运算符即可 c.display(); return 0; } void Complex::display() { cout<<'('< 可以发现, 我们仅仅对与代码进行了很小的修改,就实现了我们所想要达到的效果,并且代码理解方面变得更加容易,在这里,运算符重载函数的形参情况与上文中的一样,并不是我们故意少设置了一个形参,而是系统默认为我们设置了隐藏形参,使函数中的real与imag数据自带this指针。
当然对于函数的当中对于临时对象的定义,我们也可以换种方法,反正最后运算结束都是要返回一个复数型给予一个新创建的对象,那么我们也可以不进行临时对象的创建,可以返回一个无名对象,即将函数修改为
Complex Complex::operator + (Complex &B)//定义运算符重载函数 { return Complex(real+B.real,imag+B.imag); }以上的形式也可以达到同样的效果。
在我们实现这个复数类对象的加法之后,运算符的重载使系统不但能够对int,float,double,long,char进行运算,也可以对于复数类型进行运算,对于单目运算符,其执行在于一侧的数据类型,对于双目运算符,其执行在于两侧的数据类型。比如将一个复数与一个整数相加是非法的,编译会出现错误。运算符重载的使用,贴近c++的基本思想,即我们仅仅提供了一个类,也提供了所有运算符能够自适应与之对应,用户不必在乎这些如何实现,仅仅关心如何使用,类的设计与其中数据成员与成员函数的设计(包括运算符的重载)是类的设计人员的事情,与我们无关,实现了数据的封装。
运算符重载的规则
1.运算符的重载不允许自定义新的运算符,只能够对于系统中的运算符进行定义。
2.允许重载的运算符如下:
.和*运算符不能够重载的原因是为了保证访问成员的功能不能被改变,与运算符和sizeof运算符的运算对象是类型而不是变量或者是一般的表达式,不具备重载的特征。
3.重载不能够改变运算符运算对象的个数。
4.重载不能够改变运算符的优先级别。
5.重载不可以改变运算符的结合性。
6.重载运算符的函数不能够有默认的参数,否则与第三点矛盾。
7.重载的运算符必须和用户定义的自定义类型对象一起使用,其参数至少应有一个是类对象(或者类对象的引用)。比如重载一个+运算符,参数为int型,将其重载为两数相减,则于系统标准库当中的运算符发生冲突产生了歧义无法正确编译,是绝对禁止的。即参数不可以全部是c++当中的标准类型。
8.用于类对象的运算符一般要重载,但是对于=与&则不用。因为=运算符已经能够实现数据成员之间的复制和对于对象的复制,也可以实现类的对象的赋值。但是当类中含有动态分配内存的指针成员时,就会发生危险,仅在在此种情况之下对于=重载。对于&运算符,它能够返回类对象在内存中的起始地址,不必重载。
9.从理论上来说,可以将一个运算符重载为执行任意的操作,但是为了避免重载之后实现的功能让人难以理解,我们应当使运算符重载之后能够实现类似于的原有功能。
运算符重载函数作为类的成员函数和友元函数
在运算符重载函数当中,我们可以将其分为两种类型:
第一种是作为类的成员函数,第二种是为普通函数,在类当中声明其为友元函数
在作为类的成员函数的时候,仅有一个参数,正如上两节之中提到的,在此就不再赘述。
那么作为友元函数的时候我们可以这样写
#includeusing namespace std; class Complex { private: double real; double imag; public: Complex(){real=0,imag=0;} Complex(double a,double b){real=a,imag=b;} friend Complex operator + (Complex &A,Complex &B);//将原有的成员函数声明改为运算符的重载函数的声明 void display(); }; int main() { Complex a(1.2,2.4),b(2.4,3.6),c; c = a + b;//在使用时仅仅用我们重载过的运算符即可 c.display(); return 0; } void Complex::display() { cout<<'('< 我们可以发现,在将运算符重载函数声明为友元函数时,我们必须要写两个形参,定义时也是,不可以省略。且往往将第一个实参传入的数据放入运算符左侧,第二个实参传入的数据放入运算符右侧。运算符重载后,对于实参数据的使用有着严格的位置要求,即不遵循加法的交换律。
如果将运算符重载函数作为类的成员函数,可以少写一个参数,但是必须第一个参数是一个类的对象,而且于运算符函数的类型相同。因为必须通过类的对象去调用该类的数据成员,而且只有运算符重载函数返回值与该对象类型相同,运算结果才有意义。 如果运算符左侧是一个其他的类的对象或者是c++的标准类型,那么运算符重载函数不能够作为成员函数,而只能作为非成员函数。
由于友元的使用从某些方面来说破坏了类的封装,因此从原则上来说,要尽量将运算符的重载函数设置为类的成员函数,但是当运算符的重载函数需要访问到类的私有数据成员时,则必须为友元函数。
1.c++规定,=、[]、->、()的重载函数必须为类的成员函数。
2.流的插入与流的提取>>、<<的重载函数必须重载为类的友元成员函数
3.一般将单目运算符和复合运算符的重载函数为类的成员函数
4.一般将双目运算符的重载函数为类的友元函数
重载双目运算符
上节之中我们已经提到,对于双目运算符的重载函数要为类的友元函数
对于关系运算符的重载我们通常会使用bool
比如定义一个String类,重载>、<、==运算符来判断类的对象之间的关系
#include#include using namespace std; class String { private: char *p;//用于指向字符串的指针 public: String(){p=NULL;} String(char *str); friend bool operator == (String &A,String &B); friend bool operator > (String &A,String &B); friend bool operator < (String &A,String &B); void display(); }; void Compare(String &A,String &B); int main() { String A("hello"),B("book"),C("computer"),D("hello"); Compare(A,B); Compare(B,C); Compare(A,D); } String::String(char *str){p=str;} bool operator == (String &A,String &B) { if(strcmp(A.p,B.p)==0)return true; else return false; } bool operator > (String &A,String &B) { if(strcmp(A.p,B.p)>0)return true; else return false; } bool operator < (String &A,String &B) { if(strcmp(A.p,B.p)<0)return true; else return false; } void Compare(String &A,String &B) { if(operator == (A,B)==1)cout<<"长度相等"< (A,B)==1){A.display(),cout<<"长于",B.display(),cout< 在这里我们定义运算符的重载函数返回类型为bool型,以便Compare函数调用
重载单目运算符(自增和自减)
标准模板为
声明时
类名 operator 需要重载的运算符 ();//前置 类名 operator 需要重载的运算符 ();//后置在定义时注意要有返回值,对于数据进行操作之后返回*this指针即可
#includeusing 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 operator ++ ();//前置 Complex operator ++ (int);//后置 Complex operator -- ();//前置 Complex operator -- (int);//后置 void display(); }; int main() { Complex a(1.2,2.4),b(2.4,3.6),c(3.6,4.8),d(4.8,6.0); a.display(); b.display(); c.display(); d.display(); ++a; a.display(); b++; b.display(); --c; c.display(); d--; d.display(); return 0; } void Complex::display() { cout<<'('< 重载插入流运算符和提取流运算符
模板
声明时:
istream& operator >> (istream&,类名&);//插入流运算符
ostream& operator << (ostream&,类名&);//提取流运算符
定义时
istream& operator >> (istream& 插入流名称,类名& 形参对象名);
ostream& operator >> (ostream& 提取流名称,类名& 形参对象名);
注意return 插入流名称或者return提取流名称保证连续插入与连续提取的实现
#includeusing namespace std; class Complex { private: double real; double imag; public: Complex(){real=0,imag=0;} Complex(double a,double b){real=a,imag=b;} friend istream& operator >> (istream&,Complex&); friend ostream& operator << (ostream&,Complex&); }; int main() { Complex a; cin>>a; cout<> (istream& input,Complex& A) { input>>A.real>>A.imag; return input; } ostream& operator << (ostream& output,Complex &A) { output<<"("<



