所谓重载,就是重新赋予新的含义。函数重载就是对一个已有的函数赋予新的含义,使之实现新功能,因此,一个函数名就可以用来代表不同功能的函数,也就是”一名多用”。
运算符也可以重载。实际上,我们已经在不知不觉之中使用了运算符重载。例如,大家都已习惯于用加法运算符”+”对整数、单精度数和双精度数进行加法运算,如5+8,5.8+3.67等,其实计算机对整数、单精度数和双精度数的加法操作过程是很不相同的,但由于C++已经对运算符”+”进行了重载,所以就能适用于int,float,double类型的运算。
又如”<<“是C++的位运算中的位移运算符(左移),但在输出操作中又是与流对象cout配合使用的流插入运算符,”>>“也是位移运算符(右移),但在输入操作中又是与流对象cin配合使用的流提取运算符。这就是运算符重载(operator overloading)。C++系统对“<<”和“>>”进行了重载,用户在不同的场合下使用它们时,作用是不同的.对”<<“和”>>“的重载处理是放在头文件stream中的。因此,如果要在程序中用”<<“和”>>”作流插入运算符和流提取运算符,必须在本文件模块中包含头文件stream(当然还应当包括”using namespace std“)。现在要讨论的问题是:用户能否根据自己的需要对C++已提供的运算符进行重载,赋予它们新的含义,使之一名多用.
运算符重载的本质是函数重载。
重载函数的一般格式如下:
函数类型 operator 运算符名称(形参表列){
重载实体;
}
operator 运算符名称 在一起构成了新的函数名。比如
const Complex operator+(const Complex &c1,const Complex &c2);
我们会说,operator+ 重载了重载了运算符+。
1.友元重载和成员重载
class Complex
{
public:
Complex(int a,int b){
this->a = a;
this->b = b;
}
void print(){
cout<<"("<<a<<"+"<<b<<"i)"<<endl
}
//普通全局友元
friend Complex addComplex(Complex &c1,Complex &c2);
//友元重载!
friend Complex operator+(Complex &c1,Complex &c2)
friend Complex operator-(Complex &c1,Complex &c2)
//操作符重载的成员函数
//这个+和上面的友元+会造成编译器不明确,所以只能选择一种
Complex operator+(Complex &another){
Complex temp(this->a+another.a,this->b+another.b);
}
private:
int a;//实数部分
int b;//虚数部分
};
//让c1,c2相加,只能写一个(友元)全局函数
Complex addComplex(Complex &c1,Complex &c2)
{
return Complex temp(c1.a+c2.a,c1.b+c2.b);
}
//全局提供一个+号操作符重载
Complex operator+(Complex &c1,Complex &c2){
return Complex temp(c1.a+c2.a,c1.b+c2.b);
}
Complex operator-(Complex &c1,Complex &c2){
return Complex temp(c1.a-c2.a,c1.b-c2.b);
}
int main(void){
Complex c1(10,20);
Complex c2(1,2);
c1.print();
c2.print();
Complex c3 = addComplex(c1,c2);//通过全局函数调用
c3.print();
Complex c4 = operator+(c1,c2);//显示的调用了一个+号重载操作符
c4.print();
Complex c5 = c1 + c2;//operator+(c1,c2)
c5.print();
Complex c6 = c1 + c2;//operator-(c1,c2)
c6.print();
//使用成员重载
Complex c7 = c1.operator+(c2);
c7.print();
Complex c8 = c1+c2;
//正常一般操作符运算+支持,全局的调用方式,和成员函数的调用方式
//如果有全局的operator+函数,会operator+(c1,c2);
//如果c1有成员函数operator+,会调用c1.operator(c2);
//两者只能存在一个,推荐使用成员重载
c8.print();
return 0;
}
2.重载规则
2.1 C++不允许用户自己定义新的运算符,只能对已有的 C++运算符进行重载。
例如,有人觉得BASIC中用“”作为幂运算符很方便,也想在C++中将“”定义为幂运算符,用“3**5”表示3^5,这是不行的
2.2 C++允许重载的运算符
C++中绝大部分运算符都是可以被重载的
不能重载的运算符只有 4 个:
前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符合sizeof运算符的运算对象是类型而不是变量或一般表达式,不具备重载的特性。
2.3 重载不能改变运算符运算对象(即操作数)的个数。
如,关系运算符“>”和“<”等是双目运算符,重载后仍为双目运算符,需要两个参数。运算符“ + ”,“ - ”,“ * ”,“ & ”等既可以作为单目运算符,也可以作为双目运算符,可以分别将他们重载为单目运算符或双目运算符。
2.4重载不能改变运算符的优先级别
例如“ * ”和“ / ”优先级高于“ + ”和“ - ”,不论怎样进行重载,各运算符之间的优先级不会改变。有时在程序中希望改变某运算符的优先级,也只能使用加括号的方法枪支改变重载运算符的运算顺序。
2.5重载不能改变运算符的结合性
如,赋值运算符“ = ”是右结合性(自右至左),重载后仍为右结合性
2.6重载运算符的函数不能有默认的参数
否则就改变了运算符参数的个数,与前面第3点矛盾
2.7重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应该有一个是类对象(或类对象的引用)
也就是说,参数不能全部是C++的标准类型,以防止用户修改其标准类型数据成员的运算符的性质,如下面这样是不对的
int operator+(int a,int b){
return (a-b);
}
原来运算符+的作用是对两个数相加,现在企图通过重载使它的作用改为两个数相减。如果允许这样重载话,如果有表达式4+3,它的结果是7还是1呢?显然,这是绝对要禁止的
2.8用于类对象的运算符一般必须重载,但有两个例外“=”和运算符“&”不必用户重载
复制运算符“=”可以用于每一个类对象,可以用它在同类对象之间相互赋值。因为系统已为每一个新生命的类重载了一个赋值运算符,它的作用是逐个赋值类中的数据。成员地址运算符&也不必重载,它能返回类对象在内存中的起始地址
2.9应当使重载运算符的功能类似于该运算符作用于标准类型数据时候所实现的功能
例如,我们会去重载“+”以实现对象的相加,而不会去重载“+”以实现对象相减的功能,因为这样不符合我们对“+”原来的认知
2.10运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是既非类 的成员函数也不是友元函数的普通函数
3.双目运算符重载和单目运算符(前++)
//使⽤用:L#R
operator#(L,R);//全局函数
L.operator#(R);//成员函数
class Complex{
public:
Complex(int a=0,int b=0){
this->a = a;
this->b = b;
}
printComplex(){
cout<<"("<<a<<"+"<<b<<"i)"<<endl
}
#if 0//友元全局
friend Complex &operator+=(Complex &c1,Complex &c2);
//全局的前++写法
friend Complex &operator++(Complex &c1);
//全局的后++写法
friend const Complex operator++(Complex &c1,int);
#endif
Complex &operator+=(Complex &another);
//前++的成员函数
Complex &operator++();//c1.operator++()
{
this->a++;
this->b++;
return *this;
}
//后++的成员函数
const Complex operator++(int);//c1.operator++()
{
Complex temp(this->a,this->b);
this->a++;
this->b++;
return temp;
}
private:
int a;
int b;
};
#if 0//友元重构
operator+=(c1,c2)
Complex &operator+=(Complex &c1,Complex &c2)
{
c1.a+=c2.a;
c1.b+=c2.b;
return c1;
}
//operator++(c1)
Complex &operator++(Complex &c1)
{
c1.a++;
c1.b++;
return c1;
}
//就是后++的写法
const Complex operator++(Complex &c1,int)//加个亚元//占位参数
{
Complex temp(c1.a,c1.b);//先返回,后++
c1.a++;
c1.b++;
return temp;
}
#endif
//实现+=操作符成员函数
//c1.operator(c2);
Complex &operator+=(Complex &another)
{
this->a += another.a;
this->b += another.b;
return *this;
}
int main(void){
Complex c1(10,20);
Complex c2(5,6);
//返回没有引用的情况!
c2 += c1;//operator+=(c2,c1);返回匿名对象,但是c2已经被改变,所以输出改变
c2.printComplex();
//返回是引用的情况
Complex c3(8,8);
c3 += c1;//直接改变了c3,没有返回匿名对象,省空间!
c3.printComplex();
Complex c4(10,10);
++c4;
//如果返回值不是&引用类型,会有什么结果?
//第一次和返回&引用类型的结果是一样
//++++c4,结果还是和第一次一样,为什么?
//Complex operator ++(Complex &c1)首先返回一个临时变量
//第二次(或更多)++都是对这个临时变量操作的,和c4无关了,所以一定要返回&引用类型
c4.printComplex();
//后++
Complex c5(11,12);
c5++;//加了const之后不能写c5++++了
c5.printComplex();
return 0;
}
4. 操作符重载输出输出操作符
istream & operator>>(istream&,⾃自定义类&);
ostream & operator<<(ostream&,⾃自定义类&);
cout<<c12;
//cout.operator<<(c12)
//operator<<(cout,c12);
通过友元来实现,避免修改c++的标准库
class Complex{
public:
Complex(int a=0,int b=0){
this->a = a;
this->b = b;
}
printComplex(){
cout<<"("<<a<<"+"<<b<<"i)"<<endl
}
//成员重构
//<<运算符,重载只能写在全局中,如果写在自己的Complex中,调用顺序就反了
//除非修改ostream类的源码,在那里面写一个ostream & operator <<(Complex &c)
ostream & operator<<(ostream &os);//c1.operator(cout)---->c1<<cout
//所以 << 写不了成员重构
{
os<<"("<<this->a<<"+"<<this->b<<")"<<endl;
return os;
}
#if 0 //友元重构
friend ostream & operator<<(ostream &os,Complex &c);
friend istream & operator<<(istream &os,Complex &c);
#endif
private:
int a;
int b;
}
#if 0//友元重构
//operator<<(cout,c)
//返回ostream才能连续使用<<a<<b
ostream &operator<<(ostream &os,Complex &c)
{
os<<"("<<c.a<<"+"<<c.b<<")"<<endl;
return os;
}
istream &operator>>(istream &is,Complex &c)
{
cout<<"a:";
is>>c.a;
cout<<" ";
is>>c.b;
return is;
}
#endif
int main(void)
{
Complex c1(10,20);
Complex c2(5,6);
cout<<c1<<c2;
//c2<<cout;
Complex c3;
cin>>c3;
return 0;
}
5.等号操作符
class Student{
public:
Student(int id,char *name) {
this->id = id;
int len = strlen(name);
this->name = (char*)malloc(len+1);//将name在堆上开辟一个空间
strcpy(this->name,name);
}
//防止浅拷贝,提供一个显示的拷贝构造函数
Student (const Student &s){
this->id = s.id;
int len = strlen(s.name);
this->name = (char*)malloc(len+1);
strcpy(this->name,s.name);
}
//重构系统给的“=”运算符
Student & operator=(const Student &s){
if(this->name != NULL){
delete[] this->name;
this->name = NULL;
this->id = 0;
}
this->id = s.id;
int len = strlen(s.name);
this->name = (char*)malloc(len+1);
strcpy(this->name,s.name);
return *this;
}
void printS()
{
cout<<"id:"<<this->id<<" "<<"name:"<<this->name<<endl;
}
~Student(){
if(this->name != NULL){
delete[] this->name;
this->name = NULL;
this->id = 0;
}
}
private:
int id;
char *name;//准备在堆上开辟空间
}
int main(void){
Student s1(1,"zhang3");
s1.printS();
//Student s2 = s1;//浅拷贝,必然崩
Student s2(s1);
s1.printS();
Student s3 = s1;
s3.printS();
return 0;
}