本篇文章主要写的是关于c++运算符重载的问题,运算符重载是c++中比较重要的概念。本篇文章用了大量的实例来说明c++运算符重载,因此文章的篇幅可能会比较大。
在c++中我们都知道有函数重载的概念,在类和对象的设计中还设计到了成员函数重载。同样c++也提供了运算符的重载,所谓重载,就是赋予新的含义。实际上我们在前期的学习过程中已经不知不觉使用了运算符重载,如"<<“和”>>“本来是c++的位移运算符,但是却可以用来输入与输出,这是因为c++系统对”<<“和”>>"运算符进行了重载,用户在不同的场合下使用它们,作用是不同的。
#include
using namespace std;
class complex
{
public:
complex() { real = 0; imag = 0; }//定义构造函数
complex(double r, double i) { real = r; imag = i; }//构造函数重载
complex add(complex& c2) //定义复数加法
{
complex c;
c.real = real + c2.real;
c.imag = imag + c2.imag;
return c;//返回局部对象
}
void display()//定义输出函数
{
cout << "(" << real << "," << imag<< "i)"<< endl;
}
private:
double real;
double imag;
};
int main()
{
complex c1(3, 4), c2(5, -10), c3;
c3 = c1.add(c2);
c1.display();
c2.display();
c3.display();
return 0;
}
运算符重载的方法是定义一个运算符重载函数,也就是说,运算符重载函数是通过定义一个函数来实现的,运算符重载实质上是函数的重载。
运算符重载的格式:
函数类型 operator 运算符名称(形参列表)
在上面的格式中,operator是c++的关键字,是专门用于定义重载运算符的函数的,运算符名称就是c++已经有的运算符。注意:函数名是由operator和运算符组成
#include
using namespace std;
class complex
{
public:
complex() { real = 0; imag = 0; }//定义构造函数
complex(double r, double i) { real = r; imag = i; }//构造函数重载
complex operator +(complex& c2) //定义重载运算符+的函数
{
complex c;
c.real = real + c2.real;
c.imag = imag + c2.imag;
return c;//返回局部对象
}
void display()//定义输出函数
{
cout << "(" << real << "," << imag<< "i)"<< endl;
}
private:
double real;
double imag;
};
int main()
{
complex c1(3, 4), c2(5, -10), c3;
c3 = c1+c2;//c3=c1.operator(c2)
c1.display();
c2.display();
c3.display();
return 0;
}
分析:上述代码是通过运算符重载+,实现两个复数的加法。
说明:运算符被重载后,其原有的功能依然保留,没有丧失或改变。运算符重载对c++有重要的意义,把运算符重载和类结合起来,可以在c++程序中定义出来很有意义而且使用方便的新的数类型。运算符重载使c++具有更好的扩充性和适用性。这是c++功能强大和最吸引人的一个特点。
1.c++不允许用户自己定义新的运算符,只能对已经有的运算符进行重载。
2.c++允许重载的运算符
双目关系运算符:+,-,,/,%
关系运算符:==,!=,<,>,<=,>=,
逻辑运算符:||,&&,!
单目运算符:+,-,指针,&
自增自减运算符:++,–
位运算符:|,&,~,^,<<,>>
赋值运算符:=,+=,-=,=,/=,%=,&=,|=,^=,<<=,>>=
空间申请与释放:new,delete,new[],delete[]
其他运算符:()函数调用,->,->,逗号,[]下标
c++中不能重载的运算符有五个
.(成员访问运算符),(成员指针访问运算符),::(域运算符),sizeof(长度运算符),?:(条件运算符)
3.重载不能改变运算符运算对象的个数
4.重载不能改变运算符的优先级
5.重载不能改变运算符的结合性
6.重载运算符的函数不能有默认的参数
7.重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应有一个使类对象(或类对象的引用)。也就是说,参数不能全部使c++的标志类型,以防止用户修改用于标准运算符的性质。
8.用于类对象的运算符一般必须重载,但是有两个例外,运算符“=”和运算符“&”。
“=”可以用于每个类对象,这是因为系统已经为每一个新声明的类重载了赋值运算符,它的作用是逐个复制类对象的成员。
&也不必重载,它能返回对象在内存中的地址。
以上这些规则是很容易理解的,不必死记。
这个在上述的例子中已经将过了。
#include
using namespace std;
class complex
{
public:
friend complex operator +(complex& c1, complex& c2); //重载函数作为友元
complex() { real = 0; imag = 0; }//定义构造函数
complex(double r,double i) { real = r; imag = i; }//构造函数重载
void display()//定义输出函数
{
cout << "(" << real << "," << imag<< "i)"<< endl;
}
private:
double real;
double imag;
};
//定义运算符+重载函数
complex operator +(complex& c1, complex& c2)
{
return complex(c1.real + c2.real, c1.imag + c2.imag);
}
int main()
{
complex c1(3, 4), c2(5, -10), c3;
c3 = c1+c2;//c3=operator(c1,c2)
c1.display();
c2.display();
c3.display();
return 0;
}
分析:有的读者可能会提出这样的问题,为什么把运算符函数作为友元函数呢?理由很简单,这是因为运算符函数要访问类对象的成员,如果运算符函数不是友元函数,而是一个普通的函数,它是没有权利访问类的私有成员的。
比较上述两种方式可以发现,如果运算符重载函数作为成员函数,它可以通过this指针自由地访问本类的数据成员,因此可以少些一个参数。但是要求c1+c2的第一个参数必须是对象,而且与运算符函数的类型相同。如果运算符左侧的操作数属于c++标准类型,或者其他类的对象,那么运算符重载函数只能是非成员函数,不能为成员函数
由于友元函数的使用会破坏类的封装,因此从原则上讲,要尽量将运算符函数作为成员函数,但是还应该考虑各方面和程序员的习惯,以下可供参考:
1.c++规定,赋值运算符=,下标运算符[],函数调用运算符(),成员运算符->必须作为成员函数重载。
2.流插入运算符“<<”和流提取运算符“>>”,类型转换运算符函数不能作为类的成员函数,只能作为友元函数。
3.一般将单目运算符和复合运算符重载为成员函数。
4.一般将双目运算符重载为友元函数。
这里重载==,>,<这三种双目运算符。
#include
#include
using namespace std;
class String
{
public:
//声明运算符函数为友元函数
friend bool operator==(String &str1,String &str2);
friend bool operator>(String &str1,String &str2);
friend bool operator<(String &str1,String &str2);
String() { p = NULL; }//定义默认构造函数
String(char *str) { p = str; }//重载构造函数
void display() { cout << p << endl; }
private:
char * 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");
str1.display();
str2.display();
cout<<(str1==str2)<<endl;
cout<<(str1>str2)<<endl;
cout<<(str1<str2)<<endl;
return 0;
}
//输出结果
/*
hello
book
0
1
0
*/
这里重载++这个双单目运算符。
#include
#include
using namespace std;
class Time
{
public:
Time() { minute = 0; sec = 0; }//定义默认构造函数
Time(int m,int s): minute(m), sec(s){ }//构造函数重载
Time operator++()//定义前置++运算符
{
if (++sec >= 60)
{
sec-=60; ++minute;
}
return *this;//返回当前对象值
}
Time operator++(int)//定义后置++运算符
{
Time temp(*this);//建立临时对象
sec++;
if (sec >= 60)
{
sec -= 60; ++minute;
}
return temp;//返回的是自加前的对象
}
void display() { cout << minute << ":" << sec << endl; }
private:
int minute;
int sec;
};
int main()
{
Time t1(34, 59),t2;
t1.display();//原值
++t1;//前置++
t1.display();//执行++t1后t1的值
t2 = t1++;//后置++
t1.display();//再执行t1++后t1的值
t2.display();//t2保存的是执行t1++前t1的值
return 0;
}
//输出结果
/*
34:59
35:0
35:1
35:0
*/
#include
using namespace std;
class complex
{
public:
friend ostream& operator <<(ostream& output, complex& c); //声明运算符<<函数作为友元
friend complex operator +(complex& c1, complex& c2); //重载运算符+函数作为友元
complex() { real = 0; imag = 0; }//定义构造函数
complex(double r, double i) { real = r; imag = i; }//构造函数重载
private:
double real;
double imag;
};
//定义运算符+重载函数
complex operator +(complex& c1, complex& c2)
{
return complex(c1.real + c2.real, c1.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(6, 10), c3;
c3 = c1 + c2;//c3=operator(c1,c2)
cout << c3;//调用运算符<<重载函数
//operator(cout,c3)
return 0;
}
/*
输出结果为
(8+14i)
*/
#include
using namespace std;
class complex
{
public:
friend ostream& operator <<(ostream& output, complex& c); //声明运算符<<函数作为友元
friend istream& operator >>(istream& input, complex& c); //声明运算符>>函数作为友元
friend complex operator +(complex& c1, complex& c2); //重载运算符+函数作为友元
complex() { real = 0; imag = 0; }//定义构造函数
complex(double r, double i) { real = r; imag = i; }//构造函数重载
private:
double real;
double imag;
};
//定义运算符+重载函数
complex operator +(complex& c1, complex& c2)
{
return complex(c1.real + c2.real, c1.imag + c2.imag);
}
//定义运算符<<重载函数
ostream& operator <<(ostream& output, complex& c)
{
output << "(" << c.real << "+" << c.imag << "i)" << endl;
return output;
}
定义运算符>>重载函数
istream& operator >>(istream& input, complex& c)
{
input >> c.real >> c.imag;
return input;
}
int main()
{
complex c1, c2, c3;
cin >> c1;
cin >> c2;
c3 = c1 + c2;//c3=operator(c1,c2)
cout << c3;//调用运算符<<重载函数
//operator<<(cout,c3)
return 0;
}
通过前面的讨论,可以看到,在c++中,运算符重载是很重要的,很有实用意义的。它使类的设计等价丰富多彩,扩大了类的功能和使用范围,使程序易于理解,易于对对象进行操作,它体现了为用户着想,方便用户使用的思想。应当注意到,在运算符重载中使用引用的重要性。利用引用作为函数的形参可以在调用函数的过程中不用值传递的方式进行虚实结合,而是通过址传递的方法使形参成为实参的别名,而不必设置一个形参来存放实参传递过来的值,因此减少了时间和空间的开销。
略