运算符重载

C++ 使用 operator 关键字来重载运算符

1
2
3
4
返回值类型 operator运算符(参数)
{
...
}
  • 重载后,操作符至少有一个是用户定义的类型
  • 不能违反运算符原本的句型规则,% x 是不行的
  • 不能创建新的运算符
  • 不能修改运算符优先级

大多数运算符都能通过成员或非成员函数进行重载,但 =, (), [], -> 只能通过成员函数进行重载

= 为例

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
30
31
32
33
34
#include <iostream>

using namespace std;

class Tmp
{
private: int n;
public:
Tmp();
Tmp(int m);
int Get() const;
};

Tmp::Tmp() {
n = 0;
cout << "111" << endl;
}
Tmp::Tmp(int m) {
n = m;
cout << "222" << endl;
}

int Tmp::Get() const{
return n;
}

int main()
{
Tmp q;
q = 10;
cout << q.Get();

return 0;
}

这是一种特殊情况,如果没有使用类成员函数来重载 =,那么类可以调用匹配的构造函数,用非成员函数来重载 = 就会和构造函数冲突,因此 = 只能通过成员函数进行重载

友元

  • 友元函数
  • 友元类
  • 友元成员函数

假设现在有类成员函数重载 *

A = B * 2.75; 就会被转换为 A = B.operator(2.75);

但是 A = 2.75 * B 却是不可行的

其中一种解决方法是使用非成员函数,可以按所需的顺序获得操作数,但是需要将类成员变量改为 public,这是不安全的,然而友元函数这一特殊的非成员函数可以访问类的私有成员

创建友元

首先将友元函数的原型放在类声明中,关键字为 friend

1
friend Time operator*(double m, const Time &t);
  • 虽然函数在类声明中声明,但不是成员函数
  • 虽然不是成员函数,但与成员函数的访问权限相同

常用友元:重载 << 运算符

<< 进行重载,能与 cout 输出对象内容

最初 << 是位左移运算符,但在 ostream 中对该运算符进行了重载,能够识别所有的 C++ 基本类,这是因为 ostream 类声明中都包含了相应的重载 operator<<() 定义

如果用类成员函数来重载 <<,则只能这样 qwt << cout;,所以可以使用友元函数来重载运算符

1
2
3
4
void operator<<(ostream &os, const Time &t)
{
os << t.hours << "hours, " << t.minutes << " minutes";
}

该函数是 Time 类的友元而不是 ostream 类的友元,因为该友元函数可以直接访问 Time 对象的私有成员

但是该实现方法也有问题

1
cout << "Trip time: " << trip << " (Tuesday)\n"; // can't do 

举个例子

1
2
3
int x = 5;
int y = 8;
cout << x << y;

这意味着先执行 cout << xcout 是 ostream 对象,cout << x 也是返回 osteam 对象,所以这里的 << 都是 osteam 类的重载运算符

因此可以修改友元函数,使其返回 ostream 类对象的引用

1
2
3
4
5
ostream &operator <<(ostream &os, const Time &t)
{
os << t.hours << "hours, " << t.minutes << " minutes";
return os;
}

类的转换

假设类 StonewtStonewt(double) 构造函数,就可以将构造函数用作 自动类型转换 函数

1
2
Stonewt myCat;
myCat = 19.6;

这在符号重载中也提到过

这是 隐式转换,会使用构造函数 Stonewt(double) 创建一个临时的 Stonewt 对象,并将 19.6 作为初始化值,然后采用逐成员赋值方式将该临时对象的内容复制到 myCat

只有接受一个参数的构造函数才能作为转化函数,但如果提供默认值,也可以

1
Stonewt(int stn, double lbs = 0);

C++ 新增关键字 explicit 可以关闭这种自动特性

1
2
3
4
5
6
explicit Stonewt(double lbs); // 声明构造函数

Stonewt myCat;
myCat = 19.6; // not valid
myCat = Stonewt(19.6); // valid
myCat = (Stonewt) 19.6; // valid

explict 仍然支持显示转换