首页 > 编程 > C++ > 正文

C++教程:C++操作符的重载

2020-05-23 14:25:42
字体:
来源:转载
供稿:网友
在表达式中,我们常会用到各种操作符(运算符),例如1+3和4*2。然而,这些操作符只能用于C++内置的一些基本数据类型。如果我们自己编写一个复数类,它也会有加减法的操作,那么它能否摆脱一串冗长的函数名,而享用加号呢?

在第六章我们学到过,函数是可以重载的,即同名函数针对不同数据类型的参数实现类似的功能。在C++中,操作符也是可以重载的,同一操作符对于不同的自定义数据类型可以进行不同的操作。

作为成员函数

在我们学习操作符重载前,我们先看看原先这个复数类是如何定义的:(程序16.3.1)
//complex.h
#include <iostream>
using namespace std;
class Complex//声明一个复数类
{
   public:
   Complex(Complex &a);//拷贝构造函数
   Complex(double r=0,double i=0);
   void display();//输出复数的值
   void set(Complex &a);
   Complex plus(Complex a);//复数的加法
   Complex minus(Complex a); //复数的减法
   Complex plus(double r); //复数与实数相加
   Complex minus(double r); //复数与实数相减
   private:
   double real;//复数实部
   double img;//复数虚部
};
Complex::Complex(Complex &a)
{
   real=a.real;
   img=a.img;
}
Complex::Complex(double r,double i)
{
   real=r;
   img=i;
}
void Complex::display()
{
   cout <<real <<(img>=0?"+":"") <<img <<"i";//适合显示1-3i等虚部为负值的复数
}
void Complex::set(Complex &a)
{
   real=a.real;
   img=a.img;
}
Complex Complex::plus(Complex a)
{
   Complex temp(a.real+real,a.img+img);
   return temp;
}
Complex Complex::minus(Complex a)
{
   Complex temp(real-a.real,img-a.img);
   return temp;
}
Complex Complex::plus(double r)
{
   Complex temp(real+r,img);
   return temp;
}
Complex Complex::minus(double r)
{
   Complex temp(real-r,img);
   return temp;
}

//main.cpp
#include "complex.h"
#include <iostream>
using namespace std;
int main()
{
   Complex a(3,2),b(5,4),temp;
   temp.set(a.plus(b));//temp=a+b
   temp.display();
   cout <<endl;
   temp.set(a.minus(b));//temp=a-b
   temp.display();
   cout <<endl;
   return 0;
}
运行结果:
8+6i
-2-2i

虽然程序16.3.1已经实现了复数的加减法,但是其表达形式极为麻烦,如果有复数a、b、c和d,要计算a+b-(c+d)将会变得非常复杂。如果不是调用函数,而是使用操作符的话,就会直观得多了。

声明一个操作符重载的语句格式为:
    返回值类型operator 操作符(参数表);

事实上,在声明和定义操作符重载的时候,我们可以将其看作函数了,只不过这个函数名是一些操作符。在声明和定义操作符重载时需要注意以下几点:
  1. 操作符只能是C++中存在的一些操作符,自己编造的操作符是不能参与操作符重载的。另外,“::”(域解析操作符)、“.”(成员操作符)、“……?……:……”(条件操作符)和sizeof等操作符不允许重载。
  2. 参数表中罗列的是操作符的各个操作数。重载后操作数的个数应该与原来相同。不过如果操作符作为成员函数,则调用者本身是一个操作数,故而参数表中会减少一个操作数。(请对比程序16.3.2与程序16.3.3)
  3. 各个操作数至少要有一个是自定义类型的数据,如结构或类。
  4. 尽量不要混乱操作符的含义。如果把加号用在减法上,会使程序的可读性大大下降。

下面我们把操作符作为成员函数,来实现复数的加减法:(程序16.3.2)
//complex.h
#include <iostream>
using namespace std;
class Complex//声明一个复数类
{
   public:
   Complex(Complex &a);
   Complex(double r=0,double i=0);
   void display();
   void operator =(Complex a);//赋值操作
   Complex operator +(Complex a);//加法操作
   Complex operator -(Complex a);//减法操作
   Complex operator +(double r);//加法操作
   Complex operator -(double r);//减法操作
   private:
   double real;
   double img;
};
//未定义的函数与程序16.3.1相同
void Complex::operator =(Complex a)
{
   real=a.real;
   img=a.img;
}
Complex Complex::operator +(Complex a)
{
   Complex temp(a.real+real,a.img+img);
   return temp;
}
Complex Complex::operator -(Complex a)
{
   Complex temp(real-a.real,img-a.img);
   return temp;
}
Complex Complex::operator +(double r)
{
   Complex temp(real+r,img);
   return temp;
}
Complex Complex::operator -(double r)
{
   Complex temp(real-r,img);
   return temp;
}
//main.cpp
#include "complex.h"
#include <iostream>
using namespace std;
int main()
{
   Complex a(3,2),b(5,4),c(1,1),d(4,2),temp;
   temp=a+b;//这样的复数加法看上去很直观
   temp.display();
   cout <<endl;
   temp=a-b;
   temp.display();
   cout <<endl;
   temp=a+b-(c+d);//可以和括号一起使用了
   temp.display();
   cout <<endl;
   return 0;
}

运行结果:
8+6i
-2-2i
3+3i
以上程序的main.cpp中,复数的加法表达得非常简洁易懂,与程序16.3.1相比有了很大的进步。并且,我们发现使用了括号以后,可以更方便地描述各种复杂的运算。操作符在重载之后,结合性和优先级是不会发生变化的,符合用户本来的使用习惯。

作为友元

前面我们把操作符作为成员函数,实现了复数的加减法。如果我们把操作符作为普通的函数重载,则需要将其声明为友元。这时,参数表中的操作数个数应该与操作符原来要求的操作数个数相同。

下面我们来看一下,用友元和操作符重载来实现复数的加减法:(程序16.3.3)
//complex.h
#include <iostream.h>//由于VC编译器存在问题,这里使用标准的写法居然无法通过编译
class Complex
{
   public:
   Complex(Complex &a);
   Complex(double r=0,double i=0);
   void display();
   friend Complex operator +(Complex a,Complex b);//作为友元
   friend Complex operator -(Complex a,Complex b);
   friend Complex operator +(Complex a,double r);
   friend Complex operator -(Complex a,double r);
   private:
   double real;
   double img;
};
//未定义的函数与程序16.3.1相同
Complex operator +(Complex a,Complex b)
{
   Complex temp(a.real+b.real,a.img+b.img);
   return temp;
}
Complex operator -(Complex a,Complex b)
{
   Complex temp(a.real-b.real,a.img-b.img);
   return temp;
}
Complex operator +(Complex a,double r)
{
   Complex temp(a.real+r,a.img);
   return temp;
}
Complex operator -(Complex a,double r)
{
   Complex temp(a.real-r,a.img);
   return temp;
}
//main.cpp
#include "complex.h"
#include <iostream.h>//由于VC编译器存在问题,这里使用标准的写法无法通过编译
int main()
{
   Complex a(3,2),b(5,4),c(1,1),d(4,2),temp;
   temp=a+b;
   temp.display();
   cout <<endl;
   temp=a-b;
   temp.display();
   cout <<endl;
   temp=a+b-(c+d);
   temp.display();
   cout <<endl;
   return 0;
}

运行结果:
8+6i
-2-2i
3+3i

在上面这个程序中,加号和减号操作符由成员函数变成了友元函数。细心的读者可能注意到了,那个赋值操作符的定义跑哪儿去了?

事实上,赋值操作符有点类似于默认拷贝构造函数,也具有默认的对象赋值功能。所以即使没有对它进行重载,也能使用它对对象作赋值操作。但是如果要对赋值操作符进行重载,则必须将其作为一个成员函数,否则程序将无法通过编译。

在操作符重载中,友元的优势尽显无遗。特别是当操作数为几个不同类的对象时,友元不失为一种良好的解决办法。

又见加加和减减

在第五章我们学习了增减量操作符,并且知道它们有前后之分。那么增减量操作符是如何重载的呢?同样是一个操作数,它又是如何区分前增量和后增量的呢?

前增量操作符是“先增后赋”,在操作符重载中我们理解为先做自增,然后把操作数本身返回。后增量操作符是“先赋后增”,在这里我们理解为先把操作数的值返回,然后操作数自增。所以,前增量操作返回的是操作数本身,而后增量操作返回的只是一个临时的值。

在C++中,为了区分前增量操作符和后增量操作符的重载,规定后增量操作符多一个整型参数。这个参数仅仅是用于区分前增量和后增量操作符,不参与到实际运算中去。

下面我们就来看看,如何重载增量操作符:(程序16.3.4)
//complex.h
#include <iostream.h>//由于VC编译器存在问题,这里使用标准的写法无法通过编译
class Complex
{
   public:
   Complex(Complex &a);
   Complex(double r=0,double i=0);
   void display();
   friend Complex operator +(Complex a,Complex b);
   friend Complex operator -(Complex a,Complex b);
   friend Complex operator +(Complex a,double r);
   friend Complex operator -(Complex a,double r);
   friend Complex& operator ++(Complex &a);//前增量操作符重载
   friend Complex operator ++(Complex &a,int); //后增量操作符重载
   private:
   double real;
   double img;
};
//未定义的函数与程序16.3.3相同
Complex& operator ++(Complex &a)
{
   a.img++;
   a.real++;
   return a;//返回类型为Complex的引用,即返回操作数a本身
}
Complex operator ++(Complex &a,int)//第二个整型参数表示这是后增量操作符
{
   Complex temp(a);
   a.img++;
   a.real++;
   return temp;//返回一个临时的值
}
//main.cpp
#include "complex.h" 
#include <iostream.h>//由于VC编译器存在问题,这里使用标准的写法无法通过编译
int main()
{
   Complex a(2,2),b(2,4),temp;
   temp=(a++)+b;
   temp.display();
   cout <<endl;
   temp=b-(++a);
   temp.display();
   cout <<endl;
   a.display();
   cout <<endl;
   return 0;
}

运行结果:
4+6i
-2+0i
4+4i

根据运行结果,可以看到a++和++a被区分开来了。而调用后增量操作符的时候,操作数仍然只有一个,与那个用于区分的整型参数无关。

至此,我们已经学完了类的一些基本特性和要素。在接下来的章节中,我们要更深入地发掘面向对象程序设计的优势。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表