一 概念介绍
当运算符作用于自定义的类类型的运算对象时,需要通过运算符重载重新定义该运算的含义。重载的运算符是具有特殊名字的函数:它们的名字由关键字 operator
和其后要定义的运算符号共同组成,如operator+
。和其他函数一样,重载的运算符也包含返回类型
、函数名
、参数列表
以及函数体
。
运算符重载按照重载函数定义位置的不同可分为两类:一类是在类内,作为类的成员方法;另一种是定义在类外的非成员函数,即普通的全局函数。对于类内成员方法:函数仅一个参数,运算符左侧的运算参数是this指代的对象本身,运算符右侧的参数对应成员方法的参数。对于非成员函数:函数有两个参数,第一个参数对应运算符左侧参数、第二个参数对应运算符右侧参数。非成员函数通常需要访问类的非公有属性,但这在类外是不允许的,因此要么设定非公有属性的get方法,通过函数访问类中的非公有属性;要么将运算符重载函数声明为类的友元函数,友元函数可以访问类中的任何成员属性,前一种方法,增加了函数调用会影响代码效率,故常长采用后者,即友元函数。
C++中绝大部分运算符是可以重载的,但是有些运算符无法重载。并且有些运算符只能在类内重载,有的只能在类外重载。
(1) 左移运算符<<
和右移运算符>>
只能在类外重载、即采用非成员函数的方法。
(2) 赋值运算符=
、下标运算符[]
、调用运算符()
、成员访问箭头运算符->
,必须采用类内的成员方法。
(3) 诸如::
、.*
、.
、?:
等运算符无法重载。
关于运算符重载的几点说明:
(1) 运算符重载函数中需要注意函数的返回类型:若重载比较运算符,则返回类型是bool
型;若重载赋值运算符、复合赋值运算符、输入输出运算符,则返回类型是对象的引用;若重载的是加减乘除等运算符,则返回的是一个对象。
(2) 重载输入输出运算符时,friend ostream &operator<<(ostream &, const className &);
和friend istream &operator>>(istream &, className &);
这里的第一个参数不论是输入流引用还是输出流引用,都没有用const关键字修饰 (其他运算符重载,参数几乎都带const关键字),原因很简单:那就是在输入或输出时都要向流中传入内容,从而改变流的状态,也就显然不能用const
关键字修饰。而重载输出运算符的第二个参数是一个const常量,输入运算符的第二个参数不是const常量,这一点就更好理解了,输出运算符只是输出打印对象的值并不会改变对象的值,故可用const关键字,而输入运算符重载,我们的目的就是将数据读入到这个对象中,从而正确的初始化该对象,所以肯定不能用const关键字修饰。
二 代码演示
假设我们定义一个Point
点类,该类包含x y
两个整型变量,分别表示横纵坐标。该点类也可以理解为二维XY笛卡尔坐标系中的一个由(0, 0)原点指向该点的向量。现对该类实现各运算符重载操作,示例代码如下:
#include <iostream>
#include <cmath>
using namespace std;
class Point {
public :
Point() = default;
Point(int x, int y) : x(x), y(y) {}
Point(const Point &obj) : x(obj.x), y(obj.y) {
cout << "copy constructor" << endl;
}
Point &operator=(const Point &a) = default;
Point operator+(const Point &a) { // 向量对应坐标相加
Point ret;
ret.x = this->x + a.x;
ret.y = this->y + a.y;
return ret;
}
Point &operator+=(const Point &a) {
this->x += a.x;
this->y += a.y;
return *this;
}
Point &operator()(const int val) { // 假设调用运算符操作为横纵坐标都加上val值
this->x += val;
this->y += val;
return *this;
}
int operator[](const string &name) { // 根据下标获取对应坐标
if (name == "x") return this->x;
if (name == "y") return this->y;
return -1;
}
bool operator<(const Point &a) { // 比较两向量的长度
double dis1 = pow((pow(this->x, 2) + pow(this->y, 2)), 0.5);
double dis2 = pow((pow(a.x, 2) + pow(a.y, 2)), 0.5);
return dis1 < dis2;
}
friend ostream &operator<<(ostream &, const Point &);
friend istream &operator>>(istream &, Point &);
friend Point operator-(const Point &, const Point &);
private :
int x, y;
};
ostream &operator<<(ostream &os, const Point &a) {
os << "value of this point: (" << a.x << ", " << a.y << ")";
return os;
}
istream &operator>>(istream &is, Point &a) {
is >> a.x >> a.y;
if (!is) { a = Point(); }
return is;
}
Point operator-(const Point &a, const Point &b) {
Point ret;
ret.x = a.x - b.x;
ret.y = a.y - b.y;
return ret;
}
int main() {
Point a(1, 2);
cout << "a: " << a << endl;
Point b;
cin >> b;
cout << "b: " << b << endl;
Point c = a - b;
cout << "c: " << c << endl;
a += b;
cout << "a: " << a << endl;
if (a < c) {
cout << "len(a) < len(c)" << endl;
} else {
cout << "len(a) >= len(c)" << endl;
}
cout << "(" << a["x"] << ", " << a["y"] << ")" << endl;
cout << "a: " << a << endl;
a(5);
cout << "a: " << a << endl;
return 0;
}
/* 代码输出
a: value of this point: (1, 2)
-5 -6 // 输入b对象
b: value of this point: (-5, -6)
c: value of this point: (6, 8)
a: value of this point: (-4, -4)
len(a) < len(c)
(-4, -4)
a: value of this point: (-4, -4)
a: value of this point: (1, 1)
*/
如上图所示,Point类重载了:赋值运算符 =
、加法运算符 +
、复合赋值运算符 +=
、调用运算符 ()
、下标运算符 []
、小于运算符<
、左移运算符 <<
、右移运算符 >>
、减法运算符-
,其中后三者在类外实现,即采用非成员函数实现,剩下的在类内,采用成员方法实现。在运算符重载函数中,值得注意的是函数的返回值类型,特别是何时返回值value,何时返回引用&,千万莫要混淆。