C++核心编程——运算符重载
运算符重载的方法
运算符重载的方法是定义一个重载运算符的函数,使指定运算符不能能实现原有的功能,还能实现在函数中指定新的功能。运算符重载的实质是函数的重载。
重载运算符的函数一般格式如下:
函数类型 operator 运算符名称(形参表)
{ 对运算符的重载处理 }
典例:对运算符 “+” 实现重载,使之能用于两个复数相加。
#include <iostream>
using namespace std;
class Complex{
public:
Complex(){real=0;imag=0;}
//自定义有参构造 无参构造不再提供,若要用,需要自定义
Complex(int, int);
Complex operator+(Complex &c);
void display();
private:
int real;
int imag;
};
Complex::Complex(int real, int imag)
{
this->real = real;
this->imag = imag;
}
Complex Complex::operator+(Complex &c)
{
Complex tempc;
tempc.real = real + c.real;
tempc.imag = imag + c.imag;
return tempc;
}
void Complex::display()
{
cout << "(" << real << "," << imag << "i)" << endl;
}
int main()
{
Complex c1(3,4);
Complex c2(5,-10);
Complex c3;
c3 = c1 + c2; //相当于:c3 = c1.operator+(c2);
cout << "c1="; c1.display();
cout << "c2="; c2.display();
cout << "c1+c2="; c3.display();
return 0;
}
说明:
- 自定义有参构造函数,则无参构造不再提供,若要用,需要重新自定义
- 程序
c3 = c1 + c2
相当于c3 = c1.operator+(c2);
,使用运算符重载能使用户程序易于编写,阅读和维护。
运算符重载函数作成员函数与友元函数
如果将运算符重载函数作为成员函数,它可以通过this指针自由地访问本类的数据成员,因此可以少写一个函数的参数。 但必须要求运算表达式(如c1+c2)中第1个参数即运算符左侧的操作数)是一个类对象,而且与运算符函数的类型相同。因为必须通过类的对象去调用该类的成员函数,而且只有运算符重载函数返回值与该对象同类型,运算结果才有意义。
- 如果想要计算复数+整数,如c1+i,需要重新重载运算符
Complex Complex::operator+(int i) { Complex tempc; tempc.real = real + i; tempc.imag = imag; return tempc; }
- 如果想要计算整数+复数,如i+c1, 则第一个参数不是类对象,必须使用友元函数重载运算符。
Complex operator+(int i, Complex &c) { Complex tempc; tempc.real = i + c.real; tempc.imag = c.imag; return tempc; }
- 一般将双目运算符重载为友元函数
Complex operator+(Complex &c1, Complex &c2) { Complex tempc; tempc.real = c1.real + c2.real; tempc.imag = c1.imag + c2.imag; return tempc; }
从原则上讲,要尽量将重载运算符函数作为成员函数,但还因考虑其他各方因素和习惯,以下方式可供参考:
(1)C++规定,赋值运算符“=”、下标运算符“[]”、函数调用运算符“()”、成员运算符“->”、必须作为成员函数重载。
(2)流插人“<<”和流提取运算符“>>”类型转换运算符函数不能定义为类的成员函数,只能作为友元函数。
(3)一般将单目运算符和复合运算符( +=,-=,/=,*=,&=,!=,^=,%=,>>=,<<=)重载为成员函数。
(4)一般将双目运算符重载为友元函数
完整代码(可供参考)
#include <iostream>
using namespace std;
class Complex{
public:
Complex(){real=0;imag=0;}
Complex(int, int);
Complex::operator+(int i);
friend operator+(int i, Complex &c);
friend Complex operator+(Complex &c1, Complex &c2);
void display();
private:
int real;
int imag;
};
//构造函数
Complex::Complex(int real, int imag)
{
this->real = real;
this->imag = imag;
}
//复数+整数
Complex Complex::operator+(int i)
{
Complex tempc;
tempc.real = real + i;
tempc.imag = imag;
return tempc;
}
//整数 +复数
Complex operator+(int i, Complex &c)
{
Complex tempc;
tempc.real = i + c.real;
tempc.imag = c.imag;
return tempc;
}
//复数+复数 一般情况下双目运算符重载为友元函数
Complex operator+(Complex &c1, Complex &c2)
{
Complex tempc;
tempc.real = c1.real + c2.real;
tempc.imag = c1.imag + c2.imag;
return tempc;
}
void Complex::display()
{
cout << "(" << real << "," << imag << "i)" << endl;
}
int main()
{
Complex c1(3,4);
Complex c2(5,-10);
Complex c3,c4,c5;
c3 = c1 + c2; //相当于:c3 = c1.operator+(c2);
c4 = c1 + 4;
c5 = 5 + c2;
cout << "c1="; c1.display();
cout << "c2="; c2.display();
cout << "c1+c2="; c3.display();
cout << "c1+4="; c4.display();
cout << "5+c2="; c5.display();
return 0;
}
重载双目运算符
双目运算符是C++中最常见的运算符,双目运算符有两个操作数,通常在运算符的左右侧,重载函数应该有两个参数,也通常设置为友元函数作为运算符重载函数。
典例:声明一个字符串类String,用来存放不定长的字符串,重载运算符“==”和“>”,用于两个字符串的等于、小于和大于的比较运算。
为了程序的设计了与便于理解,程序设计分步骤编写。
1. 先建立一个String类
#include <iostream>
#include <string.h>
using namespace std;
class String{
public:
//构造及打印函数
String(){p=NULL;} //无参构造
String(char *str){p=str;} //有参构造
void display(){cout << p << endl;} //打印字符串
private:
char *p;
};
int main()
{
//测试代码
String str1("Hello"),str2("world");
str1.display();
str2.display();
return 0;
}
2. 重载运算符>
通过全局函数作类友元的方式重载运算符>,其重载函数实现体如下所示。
bool operator>(String &str1, String &str2)
{
if(strcmp(str1.p,str2.p)>0)
return true;
else
return false;
}
这只是一个并不很完善的程序,但是,已经完成实质性的工作了,运算符重载成功了。既然对运算符“>”的重载成功了,其他两个运算符的重载如法炮制即可
3. 重载其他运算符
-
重载运算符“<”
重载运算符< bool operator<(String &str1, String &str2) { if(strcmp(str1.p,str2.p)<0) return true; else return false; }
-
重载运算符“==”
//重载运算符= bool operator==(String &str1, String &str2) { if(strcmp(str1.p,str2.p)==0) return true; else return false; }
-
同时记得将上述三个重载函数声明为类的友元函数:
//运算符重载函数 friend bool operator>(String &str1, String &str2); friend bool operator<(String &str1, String &str2); friend bool operator==(String &str1, String &str2);
-
测试代码:
int main() { //测试代码 String str1("Hello"),str2("world"); cout << (str1>str2) << endl; cout << (str1<str2) << endl; cout << (str1==str2) << endl; return 0; }
-
测试结果
0
1
0
4. 修饰完善,优化输出
//封装比较函数 优化输出
void compare(String &str1, String &str2)
{
if((str1 > str2)==1)
{
str1.display(); cout << " > "; str2.display();
}
else if ((str1 < str2)==1)
{
str1.display(); cout << " < "; str2.display();
}
else
{
str1.display(); cout << " = "; str2.display();
}
}
增加了一个compare函数用来对两个字符串进行比较,并输出相应的信息。这样可以减轻主函数的负担,同时使主函数简明易读。
先搭框架、逐步扩充、由简到繁、最后完善。边编程、边调试、边扩充。
完整参考程序:
#include <iostream>
#include <string.h>
using namespace std;
class String{
public:
//构造及打印函数
String(){p=NULL;} //无参构造
String(char *str){p=str;} //有参构造
void display(){cout << p;} //打印字符串
//运算符重载函数
friend bool operator>(String &str1, String &str2);
friend bool operator<(String &str1, String &str2);
friend bool operator==(String &str1, String &str2);
private:
char *p;
};
void compare(String &str1, String &str2)
{
if((str1 > str2)==1)
{
str1.display(); cout << " > "; str2.display();
}
else if ((str1 < str2)==1)
{
str1.display(); cout << " < "; str2.display();
}
else
{
str1.display(); cout << " = "; str2.display();
}
}
int main()
{
//测试代码
String str1("hello"),str2("world");
compare(str1,str2);
return 0;
}
//重载运算符>
bool operator>(String &str1, String &str2)
{
if(strcmp(str1.p,str2.p)>0)
return true;
else
return false;
}
重载运算符<
bool operator<(String &str1, String &str2)
{
if(strcmp(str1.p,str2.p)<0)
return true;
else
return false;
}
//重载运算符=
bool operator==(String &str1, String &str2)
{
if(strcmp(str1.p,str2.p)==0)
return true;
else
return false;
}
重载单目运算符
重载单目运算符与重载双目运算符相似,但由于单目运算符只有一个操作数,因此单目运算符重载函数只有一个参数,如果运算符重载函数作为成员函数,则还可省略此参数。
典例:以自增运算符++为例,设计一个Time类,含数据成员minute(分)和sec(秒),模拟秒表,每次走1秒,满60秒进1分钟,此时秒又从0起算。要求输出分和秒的值。
#include <iostream>
#include <string.h>
using namespace std;
class Time{
public:
//构造及打印函数
Time(){min=0;sec=0;}
Time(int,int);
void display();
//运算符重载
Time operator++();
private:
int min;
int sec;
};
//构造函数
Time::Time(int min, int sec)
{
this->min = min;
this->sec = sec;
}
//运算符重载
Time Time::operator++()
{
sec++;
if(sec>=60)
{
min++;
sec=0;
}
return *this;
}
//打印时间
void Time::display()
{
cout << min << ":" << sec << endl;
}
int main()
{
int i;
Time time1(34,12);
for(i=0;i<61;i++)
{
++time1;
time1.display();
}
return 0;
}
程序对运算符“++”进行了重载,使它能用于Time类对象。
但问题是:“++”和“–”运算符有两种使用方式,前置自增运算符和后置自增运算符,它们的作用是不一样的,在重载时怎样区别这二者呢?
针对“++”和“–”这一特点,C++约定:在自增(自减)运算符重载函数中增加一个int型形参就是后置自增(自减)运算符函数。
在上述程序的基础上增加对后置自增运算符的重载。
- ++运算符重载后置 重载函数
//运算符重载 ++ 后置 Time Time::operator++(int) { //建立临时对象 temp 并读取当前值 Time temp(*this); sec++; if(sec>=60) { min++; sec=0; } return temp; }
- 主函数程序程序
int main() { int i; Time time1(34,12); Time time2; time2=time1++; cout << "time2=tim1++: time1="; time1.display(); cout << " time2="; time2.display(); return 0; }
重载后置自增运算符时,多了一个int型的参数,增加这个参数只是为了与前置自增运算符重载函数有所区别,此外没有任何作用。在定义函数时也不必使用此参数因此可省写参数名,只须在括号中写int 即可。
重载流插入运算符和"<<“和流提取运算符”>>"
如果想用输出和输入自已声明的类型的数据,必须对它们重载。
对"<<“和”>>"重载的函数形式如下:
ostream & operator<<(ostream &, 自定义类 &)
istream & operator>>(istream &, 自定义类 &)
重载“>>”和“<<”的函数只能作为友元函数,不能将他们定义为成员函数
重载流插入运算符和"<<"
典例:在上述代码的基础上,重载输出运算符“<<”输出复数
#include <iostream>
#include <string.h>
using namespace std;
class Complex{
public:
//构造及打印函数
Complex(){real=0;imag=0;} //无参构造
Complex(double i,double j){real=i;imag=j;} //有参构造
Complex operator+(Complex &); //+号运算符重载
friend ostream &operator<<(ostream &output, Complex &c); //重载cout
private:
double real;
double imag;
};
Complex Complex::operator+(Complex &c)
{
Complex temp;
temp.real = real + c.real;
temp.imag = imag + c.imag;
return temp;
}
ostream &operator<<(ostream &output, Complex &c)
{
output << "(" << c.real << "," << c.imag << "i)";
return output;
}
int main()
{
//测试代码
Complex c1(1.2, 2.3);
Complex c2(0.8, 2.7);
Complex c3;
c3 = c1 + c2;
cout << c1 << "+" << c2 << "=" << c3;
return 0;
}
程序中重载了运算符“<<”运算符重载函数“operator<<”中的形参output是ostream类对象的引用,形参名output是用户任意起的。
运算符“<<”的左面是cout,前面已提到cout是在头文件iostream中声明的ostream类对象。“<<”的右面是c3,它是Complex类对象。由于已将运算符“<<”的重载函数声明为Complex类的友元函数编译系统把cout<<c3解释为:
operator<<(cout, c1)
即以cout和c1作为实参调用下面的“operator<<”函数:
ostream &operator<<(ostream &output, Complex &c)
{
output << "(" << c.real << "+" << c.imag << "i)";
return output;
}
调用函数时,形参output成为实参cout的引用,形参c成为c1的引用。因此调用函数的过程相当于执行
cout<< "(" << c1.real << "," << c1.imag << "i)";
return output的作用:
连续向输出流插入信息,output是ostream类的对象的引用,因此return output就是return cout.
流提取运算符">>"
- >>运算符重载函数
函数的返回值仍然为输入流的引用,第一个参数为输入流的引用,第二个参数为复数的引用,当调用以下函数时,第一个参数被赋值为cin的引用,第二个参数被赋值为c1的引用,程序就相当于执行istream &operator>>(istream &input, Complex &c) { input >> c.real >> c.imag; return input; }
cin >> c1.real >> c1.imag
,执行完毕后返回istream的引用,使得能够继续连续输入c2. - 函数的调用
cin >> c1 >> c2; //其中c1,c2代表2个复数