运算符重载

运算符重载
flowwalker运算符重载
对已有的运算符赋予多重含义
使得同一运算符作用于不同数据类型时产生不同类型的行为
实质是函数重载,例如
operator+(a,b)和a.operator+(a,b)运算符可以被重载为普通函数和类的成员函数(better)
重载为普通函数时,参数个数就是实际运算符目数;
重载为成员函数时,参数个数为运算符数-1(由于
类名.函数相当于已包含自己,更具体的如,a.operator-(b))一个成为函数作用的对象,其余成为函数的实参
编译

目录
范式
1 | 返回值类型 operator 运算符 (形参表) |
运算符的操作数称为函数调用的实参,运算的结果就是函数的返回值。
约定
- 重载后的运算符含义应该符合原有的用法习惯
- 尽量保持原有的特性
- C++规定,运算符重载不改变运算符优先级
- 不能被重载的运算符:
.::?:sizeof.* - 只能重载为成员函数:
=()[]->
普通运算符重载实例
1 | // 注意以下暂时将变量置于public以方便访问,正常情况下参见 friend 接口访问 |
实现[]的重载
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
using namespace std;
class Complex
{
public:
double real, imag;
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
// 此处用到了引用作为返回值即为return的值(实参)
Complex operator-(const Complex &c);
double& operator[](int index) { return index == 0 ? real : imag; }
const double& operator[](int index) const { return index == 0 ? real : imag; }
// 此处重载了两次,前者用于非const对象的只读和写入,后者用于const对象的只读(const 只能用于此)--当然也可以传入非const对象只读
};
Complex operator+(const Complex &a, const Complex &b)
{
return Complex(a.real + b.real, a.imag + b.imag);
}
Complex Complex::operator-(const Complex &c)
{
return Complex(real - c.real, imag - c.imag);
}
int main()
{
Complex a(4,4), b(1,1), c;
c = a + b;
cout << c[0] << " , " << c[1] << endl; // 使用 [] 访问
a[0] = 10; // 通过 [] 修改实部
a[1] = 20; // 修改虚部
cout << (a-b)[0] << " , " << (a-b)[1] << endl;
return 0;
}此外,
[]是双目运算符,需要两个操作数:对象本身(左操作数)和下标索引(右操作数),重载为成员函数(只能)仅有一个参数,因为第一个操作数被this 指针隐式获取了
运算符重载为友元
- 一般情况下,运算符重载为成员函数
- 重载为成员函数不能满足需求,重载为全局函数
- 重载为全局函数不能访问类的私有成员,所以需要重载为友元
例如:重载为成员函数
c+5 //ok,5+c //error!,需重载为全局函数,为使其能够访问私有应声明为友元示范:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 class Complex{
private:
double real,imag;
public:
Complex(double r,double i):real(r),imag(i){}
Complex operator+(double r);
friend Complex operator+(double r,const Complex& c);
};
Complex Complex::operator+(double r){
return Complex(real+r,img);
}
//解释 c+5
Complex operator+(double r,const Complex &c){
return Complex(r+c.real,c.imag);
}
//解释 5+c
// 对于全局函数+,第一个参数为左值,第二个参数为右值事实上:
更加简单的写法(但可能略微开销)是:
1
2
3
4
5
6
7 //构造函数改为:
Complex(double r,double i=0):real(r),imag(i){}
//采用全局友函数+自动隐式转换的方式
friend Complex(const Complex &c1,const Complex &c2){
return Complex(c1.real+c2.real,c1.imag+c2.imag);
}
// 这样5→Complex(5)可以实现正常加减
下标运算符的高级重载
目标
重载为长度可变的整型数组类
希望
- 数组元素个数可在初始化该对象时指定
- 可以往动态数组中添加元素
- 使用该类时不用操心动态分配、释放问题
- 能够像使用数组那样使用动态数组类对象,如可以下标访问元素
⚠️ “[]”是双目运算符,两个操作数,一里一外,“a[i]”等价于a.operator[](i)。按照原有“[]”的特性,“a[i]”可作为左值使用(等号左侧,具有明确的内存地址),因此应该返回引用
实现
1 |
|
Tips:注意到push_back函数每次尾部添加一个元素都要重新分配内存并复制原有内容,效率低下,因此可以采用“翻倍扩容增长策略”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 // 成员变量增加 capacity
void CArray::push_back(int v){
if(size==capacity){
// 空间爆满再扩容
int newCapacity=(capacity==0?)1:2*capacity;
int *tmpPtr=new int[newCapacity];
if(ptr){
memcpy(tmpPtr,ptr,sizeof(int)*size);
delete []ptr;
}
ptr=tmpPtr;
capacity=newCapacity;
}
ptr[size++]=v;
}如此将扩容次数从 n 降到 log(n)
赋值运算符的重载
赋值运算符“=”只能重载为成员函数!(与该类强关联必须重载为成员函数)
标准操作
- 检查自我赋值
- 检查是否为空
- 释放旧内存→分配新内存→复制数据
- 让目标指向新内存
以下为<string>的可能实现参考,掌握以下则几乎可以掌握“=”的重载
前置:浅拷贝和深拷贝
浅拷贝(浅复制)
执行逐个字节的复制工作
- 对于指针,(拷贝指针的值即地址)将导致指向同一个地方
- 若1对象消亡,但另一指针2仍然指向该地址,对象2消亡释放同一块内存,程序崩溃
- 若将1指针改指向"others",消亡原本对象,2指针成为悬空指针

深拷贝
将一个对象中指针变量指向的内容→复制到另一个对象中指针成员变量指向的地方

🤨
目标
实现 string 类,长度可变
实现
1 |
|
关于42行误判:
关于 operator 返回值的讨论
🤨 加上 const 使得 const char* 也可以使用
🤨 有必要自己能够完美写出来吗?
流运算符的重载
流插入运算符重载
<<本没有输出功能,只因被 ostream类重载了多次(cout是ostream类的对象)
cout<<object→两个参数1
2
3void operator<<(ostream &o,int n){
output(n);
}cout<<5<<" hello"→`operator<<(operator<<(cout,5)," hello")→为了链式调用,必须返回std::ostream类的引用1
2
3
4ostream & operator<<(ostream &o,int n){
output(n);
return o;
}
重载为全局函数:
1 | //实际上操作 |
重载为成员函数(实际)
1 | class ostream{ |
调用时则例如相当于(cout.operator<<(n++)).operator<<(n)
流提取运算符重载
cin 是istream类的对象,在头文件iostream声明,** istream 将>>重载为成员函数,与cin组合用于输入数据 **
实际重载
由于无法修改 ostream 类和 istream 类,只能重载为全局函数,且声明为友元
1 |
|
强制类型转换运算符重载
在c++语言中,类型的名字(包括类本身的名字)本身也是一种运算符,即强制类型转换运算符
⚠️ 不需要指定返回值类型,因为运算符就代表返回值类型
单目运算符
只能重载为成员函数
经过适当重载后等价为
对象.operator 类型名()
实现(以double重载为例)
1 |
|
有了对 double 运算符的重载,本该出现 double 类型的变量或常量的地方,出现的Complex类型对象会被自动调用operator double成员函数,然后取其返回值使用
重载自增、自减运算符
⚠️ 此处重载应有前置和后置之分,前置为返回操作后的值,后置返回操作前的值(再操作),因此:
C++规定,⚠️ 在重载自增、自减运算符时,允许多编写一个没用的 int 类型形参→
- 处理前置表达式,调用参数个数正常的重载函数
- 处理后置表达式,调用多出一个参数的重载函数
实现
1 |
|
对比底层可以发现,后置效率更低












