以“+”为例
作为成员函数重载:Entity operator+(Entity e)const;
作为全局函数重载:Entity operator+(Entity e1, Entity e2);
//需要将该函数声明为Entity类的友元
重载后的运算符使用:
Entity e1, e2;
e1 + e2;
作为成员函数重载, e1 + e2; 等价于 e1.operator+(e2);
作为全局函数重载, e1 + e2; 等价于 operator+(e1,e2);
自己的理解:重载函数可以看做是名称和用法有特殊规定的函数,写法也很自由。如上例中的对“+”的重载:函数形参除了Entity类以外可以是任何类型数据,可以是基本类型,也可以是其他自定义的类,只要能满足Entity+T的需求即可。同理,返回类型可以是void、bool、Entity&等类型。
因为运算符重载本质上是一个函数,所以必须注意传入的操作数顺序。
例如作为成员函数重载运算符“+”:
Entity operator+(int i)const;
将Entity类对象(设为e)与一个int类型变量(设为i)相加时,必须以e+i的顺序相加。如果将int作为第一操作数:i+e,则会报错“没有与这些操作数匹配的运算符”。若想实现i+e,只能以全局函数的方式重载运算符“+”:
friend Entity operator+(int i, Entity e);
补充,另一种解决方法
可以在类中添加一个转换构造函数,与一个作为全局函数重载的operator+配合实现运算的对称
在Entity类中:
Entity(int i)
:num(i)
{}
friend Entity operator+(const Entity &e1, const Entity &e2);
重载运算符的实现:
Entity operator+(const Entity& e1, const Entity& e2)
{
return Entity(e1.num+ e2.num);
}
之后就能随意使用i+e和e+i的组合了。
编译器会尝试对int类型变量使用隐式转换转换为Entity类型,类似下方代码的效果。
Entity e1 = 1000;
重载的运算符操作数、优先级和结合性不变。
运算符重载的初衷是给类添加新的功能,方便类的运算,所以应优先考虑作为类的成员函数。
必须作为全局函数的运算符重载,通常是为了保证参数的对称性。
重载运算符“=”
当类中包含指向动态空间的指针时,常需要重载运算符“=”用于深拷贝。“=”只能作为成员函数重载。
一些注意事项:
1、返回值应声明为引用,函数体中总是使用“return *this;”返回;
2、如果形参声明为指向同类对象的引用或指针,应判别所指向的对象是否与被赋值的对象为同一对象;
(if(&e == this) return *this);
3、被赋值对象应该先释放占用的内存空间或其他资源;
if(p) delete p;//p指向被赋值的Entity类对象
4、如果形参声明为指针或引用,通常应加上const修饰符;
5、如果形参声明为指针,应判别是否为空;
6、重载了“=”的类通常也要自定义复制构造函数,反之亦然。
+=、-=、*=等复合赋值运算符也遵循上述注意事项。
增量运算符“++”
Entity类中与本例相关部分
class Entity
{
private:
int num;
public:
Entity(int i)
:num(i)
{}
}
前缀++
Entity& operator++()
{
num += 1;
return *this;
}
//如果不将返回类型声明为引用,函数在返回时会创建一个新的Entitiy对象,且无法达成链式编程效果:++(++a)
后缀++
Entity operator++(int)
{
num += 1;
return Entity(num);
}
后缀++中的int并没有参与运算,是为了与前缀++作区分的占位参数。
与前缀++不同,后缀++中创建了一个新的Entity对象,并将其作为返回值。
C++本身的前缀/后缀++也是同样的道理,所以使用前缀++的效率较高。
二者作为全局函数重载时,唯一的操作数Entity对象必须作为第一参数。
friend Entity& operator++(Entity& e);
friend Entity& operator++(Entity& e, int);
重载运算符“- -”同理
重载关系运算符应返回bool
必须作为成员函数重载的运算符
=、[]、()、->
流运算符“>>”和“<<”
只能作为类的全局函数重载
犯过的错误
作为全局函数重载一个类的运算符,重载函数的形参中必须有一个该类对象作为参数(否则就没有作为运算符重载的必要了)