操作符重载
包括 +,-,/,%
等在内的(二元)操作符本质上是函数。调用这种“函数”时,要以不同的语法列出实参。操作符函数的实参要在操作符前后列出;普通函数的实参则在函数名之后的圆括号中列出。操作符函数的定义与普通函数相似,只是要在操作符之前附加保留字 operator
。可为类类型重载预定义操作符(比如+
),为这些操作符赋予新含义。
虽然并非必须,但操作符可作为类的友元。下面的示例代码展示如何将操作符 +,==
重载为友元。
#include <iostream>
using namespace std;
class Example
{
public:
friend Example operator +(const Example& ex1, const Example& ex2);
friend bool operator ==(const Example& ex1, const Example& ex2);
int get_a();
int get_b();
Example(int _a, int _b);
Example();
private:
int a,b;
};
int main()
{
Example ex1(1, 2), ex2(3, 4);
Example ex3 = ex1 + ex2;
cout<<ex3.get_a()<<" "<<ex3.get_b()<<endl; // 4 6
if(ex1 == ex2)
cout<<"ex1 is equal to ex2"<<endl;
else
cout<<"ex1 is not equal to ex2"<<endl;
return 0;
}
Example operator + (const Example& ex1, const Example& ex2)
{
Example res;
res.a = ex1.a + ex2.a;
res.b = ex1.b + ex2.b;
return res;
}
bool operator == (const Example& ex1, const Example& ex2)
{
if(ex1.a == ex2.a && ex1.b == ex2.b)
return true;
else
return false;
}
Example::Example() : a(0), b(0)
{
}
Example::Example(int _a, int _b) : a(_a), b(_b)
{
}
int Example::get_a()
{
return a;
}
int Example::get_b()
{
return b;
}
大多数(但并非全部)操作符都可重载。操作符不一定是类的友元,但一般情况下都希望如此。具体的操作符重载规则如下:
- 重载操作符时,至少一个实参必须是类类型。
- 重载的操作符可以是(但不一定是)类的友元;操作符函数可以是类的成员,也可以是普通(非友元)函数。
- 不能新建操作符。只能重载现有操作符,比如
+,-,*,/,%
等。 - 不能改变操作符获取的实参数量。例如,重载
%
时,不能把它从二元操作符变成一元操作符;重载++
时,不能把它从一元操作符变成二元操作符。 - 不能改变操作符的优先级。重载的操作符具有和原始版本一样的优先级。例如,
x * y + z
总是表示(x * y) + z
,即使x
,y
和z
是对象,而且操作符+
和*
已针对相应的类进行了重载。 - 以下操作符不可重载:圆点操作符(
.
)、作用域解析操作符(::
)以及.*
和?
。
这里需要重点说明的是,将操作符函数作为类的成员与友元函数的区别。下面以操作符函数作为类的成员重现上面的示例代码:
#include <iostream>
using namespace std;
class Example
{
public:
Example operator +(const Example& ex2);
bool operator ==(const Example& ex2);
int get_a();
int get_b();
Example(int _a, int _b);
Example();
private:
int a,b;
};
int main()
{
Example ex1(1, 2), ex2(3, 4);
Example ex3 = ex1 + ex2;
cout<<ex3.get_a()<<" "<<ex3.get_b()<<endl; // 4 6
if(ex1 == ex2)
cout<<"ex1 is equal to ex2"<<endl;
else
cout<<"ex1 is not equal to ex2"<<endl;
return 0;
}
Example Example::operator + (const Example& ex2)
{
Example res;
res.a = a + ex2.a;
res.b = b + ex2.b;
return res;
}
bool Example::operator == (const Example& ex2)
{
if(a == ex2.a && b == ex2.b)
return true;
else
return false;
}
Example::Example() : a(0), b(0)
{
}
Example::Example(int _a, int _b) : a(_a), b(_b)
{
}
int Example::get_a()
{
return a;
}
int Example::get_b()
{
return b;
}
作为类的成员,函数声明定义时就只需要包含一个参数即可,ex1+ex2
相当于 ex1
作为对象调用其成员函数 +
获取参数 ex2
。因为是类的成员,所以其函数定义时,获取 ex1.a
就直接使用 a
就可以。
除了写法上的区别,作为类的成员,其只能位于第一个参数的位置(作为调用对象),比如下面的一个例子。无论是作为类的成员还是友元函数都是合法的。
Example ex1(2,1), ex2;
ex2 = ex1 + 20;
而下面的例子,如果作为类的成员就是不合法的,而作为友元函数则合法。
Example ex1(2,1), ex2;
ex2 = 20 + ex1;
用于自动类型转换的构造函数
如果类定义包含了恰当的构造函数,系统会自动执行特定的类型转换。例如下面的示例:
Example ex1(2,1), ex2;
ex2 = ex1 + 20;
代码看起来简单和自然,但存在一个问题,就是我们之前的代码没有重载对象与整型之间的 +
操作,而只是重载了对象之间的 +
操作。
有两个方法解决这个问题,一个很单纯地方法就是重载一个对象与整型之间的 +
操作,另一个方法就是利用构造函数。我们可以按照下面的方式定义一个获取一个整型的构造函数。
Example(int _a) : a(_a), b(0) {}
这样代码在发现没有进行对象和整型之间的 +
重载,会自动执行这个构造函数,生成一个匿名对象。
重载一元操作符
当然除了重载二元操作符(+,-,==
等),还可以重载一元操作符,比如求反操作符 -
,递增操作符 ++
,递减操作符 --
等。下面用代码简单说明如何重载求反操作符。
#include <iostream>
using namespace std;
class Example
{
public:
// friend Example operator +(const Example& ex1, const Example& ex2);
// friend bool operator ==(const Example& ex1, const Example& ex2);
Example operator +(const Example& ex2);
bool operator ==(const Example& ex2);
friend Example operator -(const Example& ex);
int get_a();
int get_b();
Example(int _a, int _b);
Example();
private:
int a,b;
};
int main()
{
Example ex1(1, 2), ex2(3, 4);
Example ex3 = ex1 + ex2;
cout<<ex3.get_a()<<" "<<ex3.get_b()<<endl; // 4 6
if(ex1 == ex2)
cout<<"ex1 is equal to ex2"<<endl;
else
cout<<"ex1 is not equal to ex2"<<endl;
Example ex4 = -ex1;
cout<<"ex4 is opposite to ex1, it is "<<ex4.get_a()<<" "<<ex4.get_b()<<endl; // -1 -2
return 0;
}
Example Example::operator + (const Example& ex2)
{
Example res;
res.a = a + ex2.a;
res.b = b + ex2.b;
return res;
}
bool Example::operator == (const Example& ex2)
{
if(a == ex2.a && b == ex2.b)
return true;
else
return false;
}
Example operator - (const Example& ex)
{
Example res;
res.a = -ex.a;
res.b = -ex.b;
return res;
}
Example::Example() : a(0), b(0)
{
}
Example::Example(int _a, int _b) : a(_a), b(_b)
{
}
int Example::get_a()
{
return a;
}
int Example::get_b()
{
return b;
}
重载>>和<<
为 cout
使用的插入操作符 <<
是二元操作符。例如以下语句:
cout << "Hello out there.\n" ;
操作符是 <<
,第一个操作数是输出流 cout
,第二个则是字符串值 "Hello out there.\n"
。这两个操作数都可以改变。如 fout
是 ofstream
类型的输出流,而且已通过 open
调用与一个文件连接,就可将 cout
替换成 fout
,字符串会写到与 fout
连接的文件中。当然,还可以将字符串 "Hello out there.\n"
替换成其他字符串、变量或数字。由于 <<
是操作符,所以应该能像重载 +
和 -
等操作符那样重载 <<
,但是需要注意更多细节。
- 返回值必须是流。
- 返回值类型名称后必须添加符号
&
。
// 函数声明
class ClassName
{
public:
friend istream& operator >>(istream& Parameter_1,
ClassName& Parameter_2);
friend ostream& operator <<(ostream& Parameter_3,
const ClassName& Parameter_4);
// 定义
istream& operator >>(istream& Parameter_1,
ClassName& Parameter_2)
{
}
ostream& operator <<(ostream& Parameter_3,
const ClassName& Parameter_4)
{
}