C++之运算符重载详解

C++允许对同一作用域中的某个函数和运算符指定多个定义,分别为函数重载和运算符重载,这里主要说明运算符重载在C++中的使用。


重载运算符

在C++中可以重载的运算符如下:

名称 运算符
双目算数运算符 + (加),-(减),*(乘),/(除),% (取模)
关系运算符 ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于)
逻辑运算符 ||(逻辑或),&&(逻辑与),!(逻辑非)
单目运算符 + (正),-(负),*(指针),&(取地址)
自增自减运算符 ++(自增),–(自减)
位运算符 | (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移)
赋值运算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
空间申请与释放 new, delete, new[ ] , delete[]
其他运算符 ()(函数调用),->(成员访问),,(逗号),[](下标)

不可重载运算符:

  • .:成员访问运算符
  • .*, ->*:成员指针访问运算符
  • :::域运算符
  • sizeof:长度运算符
  • ?::条件运算符
  • #: 预处理符号

一些限制:

  • 无法重载所有操作数均为基本类型的操作符
  • 无法通过重载操作符改变操作符的优先级
  • 无法通过操作符重载改变操作数的个数
  • 无法通过操作符重载发明新的操作符
  • 操作符重载着力对一致性的追求

重载函数

运算符重载函数一般有两种形式:重载为类的成员函数和重载为类的非成员函数。非成员函数通常是友元。(可以把一个运算符作为一个非成员、非友元函数重载。但是,这样的运算符函数访问类的私有和保护成员时,必须使用类的公有接口中提供的设置数据和读取数据的函数,调用这些函数时会降低性能。可以内联这些函数以提高性能。)
运算符重载为类的成员函数的一般格式:

1
2
3
<函数类型> operator <运算符> (<参数表>) {
<函数体>
}

调用成员函数运算符的格式如下:
<对象名>.operator <运算符> (<参数>)
等价于:
<对象名><运算符><参数>
例如:a+b => a.operator +(b)
运算符重载为类的友元函数的一般格式为:

1
2
3
friend <函数类型> operator <运算符> (<参数表>) {
<函数体>
}

调用友元函数运算符格式如下:
operator <运算符> (<参数1>,<参数2>)
等价于:
<参数1><运算符><参数2>
例如:a+b => operator +(a, b)


运算符重载实例

上面介绍了一些基本概念,对于运算符重载还是有很多坑,尤其是运算符重载结合指针使用的时候,很容易会出现内容泄露的问题。
例一:创建一个复数类,实现+,-,+=, -=,-,~,<<,>>

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>
using namespace std;
class Complex {
public:
Complex(int i=0, int j=0) : m_i(i), m_j(j) {}
void print() const {
cout << m_i << '+' << m_j << "i" << endl;
}
const Complex operator+(const Complex &r) const {
return Complex(m_i+r.m_i, m_j+r.m_j);
}
Complex& operator+= (const Complex & r) {
m_i += r.m_i;
m_j += r.m_j;
return *this;
}
const Complex operator- () const {
return Complex(-m_i, -m_j);
}
const Complex operator~ () const {
return Complex(m_j, m_i);
}
Complex& operator++ () {
++m_i;
++m_j;
return *this;
}
operator int() const {
return m_i;
}
const Complex operator++ (int) {
Complex old(*this);
++m_i;
++m_j;
return old;
}
friend const Complex operator - (const Complex &l, const Complex &r) {
return Complex(l.m_i-r.m_i, l.m_j-r.m_j);
}
friend Complex& operator-- (Complex &l) {
--l.m_i;
--l.m_j;
return l;
}
friend Complex& operator-= (Complex& l, const Complex &r) {
l.m_i -= r.m_i;
l.m_j -= r.m_j;
return l;
}
friend ostream& operator << (ostream &os, const Complex &r) {
return os << r.m_i << '+' << r.m_j << 'i';
}
friend istream& operator >> (istream &is, Complex &r) {
return is >> r.m_i >> r.m_j;
}
private:
int m_i;
int m_j;
};
int main() {
Complex c1(1,2), c2(2,3);
cout << c1+c2 << endl;
cout << c1-c2 << endl;
c1 += c2;
c1.print();
c1 -= c2;
c1.print();
Complex c3(5,6);
(c1+=c2)=c3;
c1.print();
cout << c1 << endl << c2 << endl;
// cin >> c1 >> c2;
// cout << c1 << endl << c2;
cout << -c1 << endl;
cout << ~c1 << endl;
cout << --c1 << endl;
cout << ++c1 << endl;
cout << c1++ << endl;
cout << int(c1) << endl;
return 0;
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
-1+-1i
3+5i
1+2i
5+6i
5+6i
2+3i
-5+-6i
6+5i
4+5i
5+6i
5+6i
6

实例二:实现一个String类,支持=

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
35
36
37
38
39
40
41
42
class String {
public:
String(const char *str = NULL) {
m_str = new char[strlen(str?str:"")+1];
strcpy(m_str, str?str:"");
}
~String() {
if (m_str) {
delete[] m_str;
m_str = NULL;
}
}
String(const String &that) : m_str(strcpy(new char[strlen(that.m_str) + 1], that.m_str)) {}
String& operator= (const String &that) {
if(&that != this) {
String temp(that);
swap(m_str, temp.m_str);
}
}
const char *c_str() const {
return m_str;
}
operator const char *() const {
return c_str();
}
private:
char *m_str;
};
int main() {
String str("Hello World");
cout << str.c_str() << endl;
String s2("Hello harlon");
str = s2;
cout << str.c_str() << endl;
return 0;
}

运行结果:

1
2
Hello World
Hello harlon


参考资料:
C++ 重载运算符和重载函数
C++的运算符重载