运算符重载
概述
操作符重载是c++的重要特性之一,它允许我们以简单的方式操作对象,隐藏了内部机制。
操作符重载的基本形式为:[可选项] 返回值 operator
运算符(可选参数列表) { 函数体 }
下面我们通过一个简单的例子来仔细讨论每一个部分。
一个简单的例子
我们先定义一个时间类,利用该类来说明操作符重载的方方面面。
// mytime.h
#pragma once
#include <iostream>
class Time
{
public:
Time();
Time(int h, int m = 0);
~Time();
void AddMin(int m);
void AddHr(int h);
void Reset(int h = 0, int m = 0);
Time Sum(const Time& t) const;
void Show() const;
//作为成员函数重载的操作符
Time operator+(const Time& t) const; // 1
Time operator*(double m); // 2
// 作为友元重载的操作符
friend Time operator*(double m, const Time& t); // 3
friend std::ostream& operator<<(std::ostream& os, const Time& t); // 4
private:
int hours;
int minutes;
};
我们重载了+、*以及<<操作符。其中+运算符作为类的成员函数重载(类的一部分),*运算符即有成员函数的重载版本,也有作为友元的重载版本(不是类的一部分),<<作为友元重载。
作为类成员函数的操作符重载
第一个和第二个重载就是作为类成员函数的重载,这时候的运算符与普通成员函数没有区别。来看第一个重载的实现:
Time Time::operator+(const Time& t) const
{
return this->Sum(t);
}
此时的+是类的一部分,所以需要加上Time::。这个重载没有可选项,返回值是Time
对象,参数是Time
类型的引用。从实现中可以看到,我们在里面调用了具有相同功能的成员函数,这是操作符重载的常见写法。
定义好重载的操作符后,我们可以使用它:
Time t1(12,23), t2(4,12);
t1 = t1+t2;
//上述语句等价于:t1 = t1.operator+(t2);
t1 = t1 * 2;
//上述语句等价于调用:t1 = t1.operator*(2);
注意*操作符的调用,调用的是作为成员函数的操作符,而不是友元,如果我们把代码改成下面这样,就会调用友元版的操作符:
t1 = 2 * t1;
// 等价于调用:t1 = operator*(2, t1);
可以看到,对于作为成员函数的操作符,其参数比原操作符少一个。其实也可以这样理解,作为成员函数的操作符,第一个参数不是我们在代码中显式写出的那个,而是隐式传递的this
指针。this
对应操作符的左操作数,我们定义的参数对应操作符的右操作数。
作为友元的操作符重载
在上一节我们已经使用了*操作符的友元版本,这里给出*操作符的友元版本的实现:
Time Time::operator*(double m)
{
Time t;
t.minutes = this->minutes * m;
t.hours = this->hours * m;
t.hours += t.minutes / 60;
t.minutes %= 60;
return t;
}
此时的*是友元,而不是类的一部分,所以我们不需要加上Time::。接下来我们着重介绍<<运算符的重载。先给出它的实现:
std::ostream& operator<<(std::ostream& os, const Time& t)
{
os << t.hours << " hours, " << t.minutes << " minutes";
}
它的第一个参数是一个流对象,第二个参数是用户自定义对象,分别对应<<运算符的左操作数和右操作数。它的返回值是流对象的引用,这样一来我们就可以写出如下的代码:
std::cout << t1 << std::endl;
这条语句的执行是从左到右的,调用的第一个<<返回了一个流对象,该流对象可以继续参与第二个<<的运算。
重载限制
重载的操作符不一定是成员函数,但是至少有一个操作数类型是用户自定义类型,这防止用户为标准类型重载运算符。
使用运算符不能违反运算符原来的语法规则,比如说不能将+重载为只有一个操作数的运算符。
// +t1;
// t1+;
不能修改运算符的优先级。
不能创建新的运算符。
不能重载以下运算符:
- sizeof
- .
- .*
- ::
- ? :
- typeid
- 强制类型转换运算符
以下运算符只能通过成员函数进行重载:=,(),[],->
如果运算符表示的意思过于抽象或复杂,建议通过函数来实现,而不是重载运算符。