看完封装和继承了,该看多态了。
运算符重载
类外定义的运算符重载函数
以如下代码为例
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
| #include <bits/stdc++.h> using namespace std; class A { public: A() = default; A(int x, int y) : a(x), b(y) {} int a; int b; };
A operator+(A op1, A op2) { A tmp; tmp.a = op1.a + op2.a; tmp.b = op1.b + op2.b; return tmp; }
int main() { A a(1, 2); A b(2, 3); A c = a + b; A d = operator+(a, b); cout << c.a << " " << c.b << endl; cout << d.a << " " << d.b << endl; return 0; }
|
a + b
与 operator+(a, b)
是等价的
绝大多数的运算符都允许重载,只有以下几个不允许重载:
.
.*
::
sizeof
?:
需要注意的是,重载运算符函数的参数至少有一个是类对象(或其引用),出于防止用户修改标准类型数据的运算符性质。
友元运算符重载函数
上文中在类外定义的运算符重载函数只能访问类中的公有成员。要使运算符重载函数能够访问私有成员,有两种方式:将其定义为成员函数(成员运算符重载函数),或定义为类的友元函数(友元运算符重载函数)。
在类的内部,定义友元运算符函数:
1 2 3
| friend 函数类型 operator 运算符 (形参表){ ... }
|
也可以在类内声明友元函数原型,在外部定义
1 2 3 4 5 6 7 8
| class A{ ... friend 函数类型 operator 运算符 (形参表); ... }; 函数类型 operator 运算符 (形参表){ ... }
|
双目运算符重载(二元运算符)
见前文
单目运算符重载
以下面的代码为例,单目运算符重载函数只有一个形参,也因此这个形参必须是类对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <bits/stdc++.h> using namespace std; class A { public: A() = default; A(int x) : a(x) {} int a; };
A operator+(A op1) { return A(op1.a); }
A operator-(A op1) { return A(-op1.a); }
int main() { A a(1); A b = -a; cout << a.a << endl; cout << b.a << endl; return 0; }
|
成员运算符重载函数
在类的内部直接定义成员运算符重载函数
1 2 3 4 5
| class A{ 函数类型 operator 运算符 (形参表){ ... } };
|
在类的内部声明成员运算符重载函数
1 2 3 4 5 6 7 8
| class A{ ... 函数类型 operator 运算符 (形参表); ... }; 函数类型 A::operator 运算符 (形参表){ ... }
|
双目运算符重载
对于双目运算符而言,成员运算符重载函数的形参表中仅有一个参数,因为另一个操作数(左操作数)是对象本身。
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
| #include <bits/stdc++.h> using namespace std; class A { public: A() = default; A(int x, int y) : a(x), b(y) {} A operator+(A op2) { A tmp; tmp.a = a + op2.a; tmp.b = b + op2.b; return tmp; } int a; int b; };
int main() { A a(1, 2); A b(2, 3); A c = a + b; A d = a.operator+(b); cout << c.a << " " << c.b << endl; cout << d.a << " " << d.b << endl; return 0; }
|
单目运算符重载
对于单目运算符而言, 操作数就是对象本身,因此没有形参。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <bits/stdc++.h> using namespace std; class A { public: A() = default; A(int x) : a(x) {} A operator-(){ return A(-a); } int a; };
int main() { A a(1); A b = -a; cout << a.a << endl; cout << b.a << endl; return 0; }
|
成员运算符重载与友元运算符重载的比较
由于成员运算符重载函数缺少一个形参(做操作数),因此如果只用成员运算符重载,无法将对象放在符号右侧。
而友元运算符重载可以定义两个形参,可以分别处理两侧操作数的类型(重载)。
++ 和 -- 的重载
++
与 --
比较特殊,其有前缀和后缀两种方式,而上文中的单目运算符重载无法区分前缀和后缀,区分后缀和前缀,需要特殊的声明形式。
以下面的例子说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <bits/stdc++.h> using namespace std; class A { public: A() = default; A(int x) : a(x) {} A operator++(){ return A(a+1); } A operator++(int x){ cout << x << endl; return A(a+2); } int a; };
int main() { A a(1); A b = ++a; A c = a++; cout << b.a << endl; cout << c.a << endl; return 0; }
|
执行这段代码有以下输出
其中,第一行的 0
,是后缀调用的的传入参数,可以看出,后缀式,处理时是会隐式的在后面加上
0
的。
相应的,如果使用友元运算符重载函数,也是加入第二个 int
类型形参。
赋值运算符的重载
对于任何一个类,当没有显式的赋值运算符重载时,编译器会自动合成一个默认的赋值运算函数,这是一种浅拷贝,可能会造成问题,因此需要引入对赋值运算符的重载。
下面给出一个深拷贝的赋值的例子
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
| #include <bits/stdc++.h> using namespace std; class A { public: A(string ss) { s = new string(ss); } ~A() { delete s; } A &operator=(const A &); void pt() { cout << s << " " << *s << endl; }
private: string *s; }; A &A::operator=(const A &op) { if (this == &op) return *this; delete s; s = new string(*op.s); return *this; } int main() { A a("aaa"); A b("bbb"); a.pt(); b.pt(); a = b; a.pt(); return 0; }
|
输出类似下面的
1 2 3
| 0xb400007c58aae370 aaa 0xb400007c58aae010 bbb 0xb400007c58aae370 bbb
|
这里有些微妙的东西,就是 a
的 s
地址不变,代码中是有 delete
但是随后
new
,p
仍指向这个地址,因此看似没有修改。
需要注意的是,双目赋值只能是成员函数,否则左操作数可能不是类对象。
下标运算符[]的重载
对于 X[Y],可以认为这是一个双目表达式
下标运算符重载函数只能定义为成员函数
1 2 3
| 返回类型 类名::operator[] (形参){ ... }
|
调用方式既可以是X[Y]
又可以是
X.operator[](Y)
。
类型转换
类类型与系统预定义类型的转换
- 通过转换构造函数进行类型转换
- 通过类型转换函数进行类型转换
转换构造函数
转换构造函数只有一个系统预定义类型的形参,用于将系统预定义类型转换为类类型
类型转换函数
也算一种运算符重载,毕竟 C++类型转换有 int(2.0)
这样的写法。
- 类型转换函数只能是成员函数而不是友元函数
- 类型转换函数既没有参数也不能指定类型
- 但类型函数中必须
return
出目标类型数据
- 一个类可以有多个类型转换函数