运算符重载
之前介绍过函数重载,所谓重载,就是重新赋予新的含义。函数重载就是对一个已有函数赋予新的含义,使之实现新的功能。运算符也可以重载,实际上,我们已经不知不觉使用了运算符重载。例如,大家都已经习惯用加法运算符“+”对整数、单精度数和双精度数进行加法操作,由于C++中对运算符“+”进行了重载,使“+”能适用于int,float,doouble类型的不同的运算。
对运算符重载的函数有两种处理方式:
(1)把运算符重载的函数作为类的成员函数;(2)运算符重载的函数不是类的成员函数(可以是一个普通函数),在类中把它声明为友元函数。有的读者会提这样的问题:为什么把运算符函数作为友元函数呢?理由很简单,因为运算符函数要访问Complex类对象中的成员。如果运算符函数不是Complex类的友元函数,而是一个普通的函数,它是没有权利访问Complex类的私有成员。
如果将运算符重载函数作为成员函数,它可以通过this指针自由地访问本类的数据成员,因此可以少写一个函数的参数。单必须要求运算表达式中第一个参数是一个类对象,而且与运算符函数类型相同。因为必须通过类的对象去调用该类的成员函数,而且只有运算符重载函数返回值与该对象同类型,运算结果才有意义。
可想而知,如果运算符左侧的操作数属于C++标准类型或是一个其他类的对象,则运算符重载函数不能作为成员函数,只能作为非成员函数。如果函数需要访问类的私有成员,则必须声明为友元函数。
将双目运算符重载为友元函数时,由于友元函数不是该类的成员函数,因此在函数的形参列表中必须有两个参数,不能省略,形参的顺序任意,不要求第一个参数必须为类对象,但在使用运算符的表达式中,要求运算符左侧的操作数与函数第一个参数对应,运算符右侧的操作数与函数的第二个参数对应。
究竟把运算符重载函数作为类的成员函数好,还是友元函数好?
由于友元的使用会破坏类的封装,因此从原则上说,要尽量将运算符重载函数作为成员函数。但还应该考虑到各个方面的因素和程序员的习惯,以下可供参考:
1.C++规定,赋值运算符“=”、下标运算符“[]”、函数调用运算符“()”、成员运算符“->”必须作为成员函数。
2.流插入“<<”和流提取运算符“>>”、类型转换运算符不能定义为类的成员函数,只能作为友元函数。
3.一般将单目运算符和复合运算符(+=,-=,/=,*=,&=,!=,^=,%=,>>=,<<=)重载为成员函数。
4.一般将双目运算符重载为友元函数。
重载双目运算符
class String
{
public:
String() //定义默认构造函数
{
p=NULL;
}
String(char *str); //声明构造函数
friend bool operator>(String &str1,String &str2);//声明运算符函数为友元函数
void display();
private:
char *p;//字符型指针,用于指向字符串
};
String ::String(char *str) //定义构造函数
{
p=str; //使p指向实参字符串
}
void String::display()
{
cout<<p;
}
bool operator>(String &str1,String &str2)
{
if(strcmp(str1.p,str2.p)>0)
return true;
else
return false;
}
int main()
{
String str1("hello"),str2("book");
str1.display();
cout<<endl;
str2.display();
cout<<endl;
cout<<(str1>str2)<<endl;
return 0;
}
这并不是一个很完善的程序,但是已经完成了实质性的工作,运算符重载成功了。其他的两个运算符的重载如法炮制即可。
扩展到3个运算符重载:
class String
{
public:
String() //定义默认构造函数
{
p=NULL;
}
String(char *str); //声明构造函数
friend bool operator>(String &str1,String &str2);//声明运算符函数为友元函数
friend bool operator<(String &str1,String &str2);
friend bool operator==(String &str1,String &str2);
void display();
private:
char *p;//字符型指针,用于指向字符串
};
String ::String(char *str) //定义构造函数
{
p=str; //使p指向实参字符串
}
void String::display()
{
cout<<p;
}
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;
}
int main()
{
String str1("hello"),str2("book");
cout<<(str1>str2)<<endl;
cout<<(str1<str2)<<endl;
cout<<(str1==str2)<<endl;
return 0;
}
结果显然是对的。到此为止,主要任务基本完成。
再进一步修饰完善,使输出结果更加直观:
class String
{
public:
String() //定义默认构造函数
{
p=NULL;
}
String(char *str); //声明构造函数
friend bool operator>(String &str1,String &str2);//声明运算符函数为友元函数
friend bool operator<(String &str1,String &str2);
friend bool operator==(String &str1,String &str2);
void display();
private:
char *p;//字符型指针,用于指向字符串
};
String ::String(char *str) //定义构造函数
{
p=str; //使p指向实参字符串
}
void String::display()
{
cout<<p;
}
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;
}
void compare(String str1,String str2)
{
if(operator>(str1,str2)==1)
{
str1.display();
cout<<">";
str2.display();
}
else if(operator<(str1,str2)==1)
{
str1.display();
cout<<"<";
str2.display();
}
else if(operator==(str1,str2)==1)
{
str1.display();
cout<<"==";
str2.display();
}
}
int main()
{
String str1("hello"),str2("book"),str3("love");
compare(str1,str2);cout<<endl;
compare(str1,str3);cout<<endl;
compare(str3,str2);cout<<endl;
return 0;
}
重载单目运算符:
class Time
{
public:
Time()
{
min=0;
sec=0;
}
Time(int m,int s):min(m),sec(s){}
Time operator++();
void display()
{
cout<<min<<":"<<sec<<endl;
}
private:
int min;
int sec;
};
Time Time::operator ++()
{
if(++sec>=60)
{
sec-=60;
++min;
}
return *this;
}
int main()
{
Time t1(34,0);
for(int i=0;i<61;i++)
{
++t1;
t1.display();
}
return 0;
}
重载流插入运算符:
class Complex
{
public:
Complex()
{
real=0;
imag=0;
}
Complex(double r,double i)
{
real=r;
imag=i;
}
Complex operator+(Complex &c2);
friend ostream& operator<<(ostream&,Complex&);
private:
double real;
double imag;
};
Complex Complex::operator +(Complex &c2)
{
return Complex(real+c2.real,imag+c2.imag);
}
ostream& operator<<(ostream& output,Complex& c)
{
output<<"("<<c.real<<"+"<<c.imag<<"i)"<<endl;
return output;
}
int main()
{
Complex c1(2,4),c2(3,5),c3;
c3=c1+c2;
cout<<c3;
return 0;
}
重载流提取运算符:
class Complex
{
public:
friend ostream& operator<<(ostream& output,Complex&);
friend istream& operator>>(istream& output,Complex&);
private:
double real;
double imag;
};
ostream& operator<<(ostream& output,Complex& c)
{
output<<"("<<c.real<<"+"<<c.imag<<"i)";
return output;
}
istream& operator>>(istream& input,Complex& c)
{
cout<<"input real part and imaginary part of complex number:";
input>>c.real>>c.imag;
return input;
}
int main()
{
Complex c1,c2;
cin>>c1>>c2;
cout<<"c1="<<c1<<endl;
cout<<"c2="<<c2<<endl;
return 0;
}
使用运算符重载的具体做法是:
1.先确定要重载的是哪一个运算符,想把它用于哪一类。重载运算符只能把一个运算符用于一个指定的类。
2.设计运算符重载函数和有关的类
3.在实际工作中,一般并不要求最终用户自己编写每一个运算符重载函数,往往是有人事先把本领域或者本工作单位中需要用重载的运算符统一编写好一批运算符重载函数,把他们集中到一个头文件,放在指定的文件目录中,提供给有关的人使用。
4.使用者需要了解在该头文件中包含哪些运算符的重载,适用于哪些类,有哪些参数。
5.如果有特殊的需要,并无现成的重载运算符可用,就需要自己设计运算符重载函数。应该注意把每次设计的运算符重载函数保留下来,以免下次用时重复设计。