运算符的重载

运算符重载基本概念

运算符重载的需求

C + +预定义的运算符,只能用于基本数据类型的运算:整型、实型、字符型、逻辑型等。
在数学上,两个复数可以直接进行+、-等运算。但是在C++中,直接将+或-用于复数对象是不允许的。
有时希望,让对象也能通过运算符进行运算。这样代码更简洁,容易理解。
例如:complex_a和complex_b是两个复数对象;求两个复数的和, 希望能直接写:
complex_a + complex_b

定义

运算符重载,就是对已有的运算符(C + +中预定义的运算符)赋予多重的含义,使同一运算符作用于不同类型的数据时导致不同类型的行为。
运算符重载的目的是:扩展C++中提供的运算符的适用范围,使之作用于对象。
同一个运算符,对不同类型操作数,所发生的行为不同。

运算符重载的形式

运算符重载的实质是函数重载
可以重载为普通函数,也可以重载为成员函数
把含运算符的表达式转换成对运算符函数的调用
把运算符的操作数转换成运算符函数的参数
运算符被多次重载时,根据实参的类型决定调用哪个运算符函数。
返回值类型 operator 运算符(形参表)
{
……
}

运算符重载示例

重载为成员函数时, 参数个数为运算符目数减一。
重载为普通函数时, 参数个数为运算符目数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;

class Complex{
public:
double real,imag;
Complex(double r=0.0,double i=0.0):real(r),imag(i){ }
Complex operator-(const Complex & c);
};
Complex operator+(const Complex &a,const Complex &b){
return Complex(a.real+b.real,a.imag+b.imag);
}
Complex Complex::operator-(const Complex &c){
return Complex(real-c.real,imag-c.imag);
}


int main()
{
Complex a(4,4),b(1,1),c;
c = a + b; //等价于c=operator+(a,b);
cout << c.real << "," << c.imag << endl;
cout << (a-b).real << "," << (a-b).imag << endl;
//a-b等价于a.operator-(b)
return 0;
}

赋值运算符的重载

赋值运算符 ‘ =’重载

有时候希望赋值运算符两边的类型可以不匹配,比如,把一个int类型变量赋值给一个Complex对象,或把一个char*类型的字符串赋值给一个字符串对象,此时就需要重载赋值运算符“=”。赋值运算符“=”只能重载为成员函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class String{
private:
char *str;
public:
String (): str(new char[1]) {
str[0] = 0;
}
const char * c_str(){
return str;
}
String & operator = (const char *s);
String::~String() {delete [] str;}
};
String & String::operator = (const char *s){
delete [] str;
str = new char[strlen(s)+1];
strcpy(str, s);
return * this;
}

int main()
{
String s;
s = "Good Luck";
cout << s.c_str << endl;
s = "Shenzhou 8!";
cout << s.c_str()<<endl;
return 0;
}

浅拷贝和深拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class String{
private:
char *str;
public:
String ():str(new char[1]) {str[0] = 0;}
const char *c_str() {return str;};
String & operator = (const char *s){
delete [] str;
str = new char[strlen(s)+1];
strcpy(str, s);
return *this;
};
~String() {delete [] str;}
}

如果不定义自己的赋值运算符,那么S1 = S2实际上导致S1.str和S2.str指向同一个地方。
如果S1对象消亡,析构函数将释放S1.str指向的空间,则S2消亡时还要释放一次,不妥。
另外,如果执行S1 = “other”,会导致S2.str指向的地方被delete掉。因此要在class String里添加成员函数:

1
2
3
4
5
6
String & operator = (const char *s){
delete [] str;
str = new char[strlen(s)+1];
strcpy(str, s);
return *this;
};

对operator = 返回值类型的讨论

对运算符进行重载的时候,好的风格是应该尽量保留运算符原本的特性。

运算符重载为友元函数

一般情况下,将运算符重载为类的成员函数,是较好的选择。
但有时,重载为成员函数不能满足使用要求,重载为普通函数,又不能访问类的私有成员,所以需要将运算符重载为友元。

1
2
3
4
5
6
7
8
9
class Complex{
double real,imag;
public:
Complex(double r,double i):real(r),imag(i){ };
Complex operator+(double r);
};
Complex Complex::operator+(double r){
return Complex(real+r,imag);
}

经过上述重载后:Complex c;c = c+5;有定义,相当于c = c.operator+(5);但是c=5+c报错;
所以,为了使得上述的表达式能成立,需要将+重载为普通函数。

1
2
3
Complex operator+ (double r,const Complex &c){
return Complex(c.real+r,c.imag);
}

但是普通函数又不能访问私有成员,所以,需要将运算符+重载为友元。

1
2
3
4
5
6
7
class Complex{
double real,imag;
public:
Complex(double r,double i):real(r),imag(i){};
Complex operator+(double r);
friend Complex operator +(double r,const Complex & c);
}

流插入运算符和流提取运算符的重载

流插入运算符的重载

cout << 5 << “this”;
cout是在iostream中定义的,ostream类的对象。 “<<” 能用在count上,是因为在iostream里对”<<”进行了重载
cout << 5;即cout.operator<<(5);
cout << “this”;即cout.operator<<(“this”);

1
2
3
4
5
6
ostream & ostream::operator<<(int n){
return *this
}
ostream & ostream::operator<<(const char *s){
return *this;
}

cout<<5<<”this”本质是 cout.operator<<(5).operator<<(“this”);

类型转换运算符和自增、自减运算符的重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;
class Complex{
double real,imag;
public:
Complex(double r=0,double i=0):real(r),imag(i) { };
operator double () {return real; }
};
int main(){
Complex c(1.2,3.4);
cout << (double)c << endl;
double n = 2 + c;
cout << n;
}

自增运算符++、自减运算符—有前置、后置之分,为了区分所重载的是前置运算符还是后置运算符,C++规定:
前置运算符作为一元运算符重载:
重载为成员函数: T & operator++();T & operator—();
重载为全局函数: T1 & operator++(T2); T1 & operator—(T2);
后置运算符作为二元运算符重载,多写一个没用的参数 int
类型强制转换运算符被重载时不能写返回值类型,实际上其返回值类型就是该类型强制转换运算符代表的类型
注意:

  1. C++不允许定义新的运算符
  2. 重载后的运算符含义应该符合日常习惯
  3. 运算符重载不改变运算符的优先级
  4. 以下运算符不能被重载: “.”、“::”等
  5. 重载运算符()、[]、->或者赋值运算符=时,运算符重载函数必须声明为类的成员函数。
0%