运算符重载
约 2100 字大约 7 分钟
2025-04-06
运算符重载,就是赋予运算符新的含义。它是重载系统内部运算符的函数,是实现类静态多态性的方式之一。它本质上是函数重载。
实际上,我们已经使用了运算符重载。列如:我们习惯使用的对整数,浮点数运用+、-、*、/
,起始计算机对整数和浮点数的操作过程是不一样的,但由于C++已经对运算符进行了重载,所以才能都适用。又如<<
本来是左移运算符,但在输出操作中,与cout
搭配,当作输出流运算符了
运算符重载的意义
我们平常常见的算术运算符、逻辑运算符等运算符都是系统库函数里面已经存在的,所以运算符可以直接用于我们常用的数据类型。然而对于我们自定义的类实例化的对象,系统没有与之相关的运算符可以操作,但是为了使我们编写程序时更加便捷,C++提供了一种方式——运算符重载,来满足我们对于类对象进行的操作。
也就是说运算符重载是为了解决类对象之间的运算的,让编译器在遇到对象运算时能按我们要求的进行运算,这就是运算符重载的意义。
运算符重载限制
C++中绝大部分的运算符允许重载,少部分不允许重载,详细描述如下
- 可以重载的运算符
- 算术运算符:
+ - * / %
- 自增、自减运算符:
++ --
- 位运算符:
| & ~ ^ << >>
- 逻辑运算符:
|| && !
- 关系运算符:
== != < > <= >=
- 赋值运算符:
= += -= /= %= &= |= ^= <<= >>=
- 单目运算符:
+ - * &
- 动态内存分配:
new delete new[] delete[]
- 其他运算符:
() -> , []
- 算术运算符:
- 不能重载的运算符
.
成员访问运算符::
域运算符._ ->_
成员指针访问运算符szieof
长度运算符?:
条件运算符
运算符重载规则
- 重载运算符函数可以对运算符作出新的解释﹐但原有基本语义不变:
- 不改变运算符的优先级
- 不改变运算符的结合性
- 不改变运算符所需要的操作数
- 不能创建新的运算符
- 语法:

- 一个运算符被重载后,原有意义没有失去,只是定义了相对一特定类的一个新运算符。
- 函数返回值:当前运算符运算结束后产物类型,
int a,int b,a+b
返回int
类型 - 函数名:
operator
和运算符组成函数名 - 函数参数
- 运算符重载函数是类中成员函数:函数参数等于操作数-1
- 运算符重载函数是友元函数:函数参数等于操作数
- 函数体:写运算符的实际想要的运算
- c++类中存在一个赋值的重载函数(默认)
- 函数返回值:当前运算符运算结束后产物类型,
运算符重载方法
- 两种重载方法
重载为成员函数或全局(友元)函数
- 两种形式的选择时机:
- 左操作数(或者只有左操作数并且)是本类的对象时,可选用成员函数形式。

- 左操作数不是本类的对象,必须采用非成员函数的形式,一般是友元函数。

- 一般单目运算符最好被重载为成员函数;双目运算符重载为友元函数。
- 有些运算符不能重载为友元函数,它们是:
=,(),[]和->
。
- 有些运算符不能重载为友元函数,它们是:
- 具有可交换性的双目运算符最好两种形式都有(成员函数时适用左操作数为本类对象,友元函数时适用左操作数为其他类的对象)。

class MM
{
public:
MM(int i,int j):i(i),j(j){}
void print()
{
cout<<this->i<<"\t"<<this->j<<endl;
}
MM operator+(MM& mm)
{
return MM(this->i+mm.i,this->j+mm.j);
}
friend void operator+=(MM&mm1,MM&mm2) //要修改数据,传入引用
{
mm1.i+=mm2.i;
mm1.j+=mm2.j;
}
protected:
int i;
int j;
};
int main(int argc, char** argv)
{
MM mm1(1,2);
MM mm2(5,8);
MM mm3=mm1+mm2;
mm3.print();
MM mm4=mm1.operator+(mm2); //显示调用
mm4.print();
//友元重载
mm1+=mm2;
mm1.print();
operator+=(mm3,mm2); //显示调用
mm3.print();
return 0;
}
C++特殊运算符重载
对于**++**
和**--**
重载,通过增加无用参数**(int)**
标识为后置运算,前置没有变化,后置在参数括号中写入**int**
,例如:**MM operator++(int)**
就是后置**++**
,下面是一个代码示例和示例结果
class MM
{
public:
MM(string name="",int money=0):name(name),money(money){}
MM operator++() //前置
{
this->money++;
return *this;
}
MM operator++(int) //后置
{
return MM(this->name,this->money++);
}
void print()
{
cout<<this->money<<"\t"<<this->name<<endl;
}
protected:
string name;
int money;
};
int main()
{
MM mm1("king",100);
MM mm2=mm1++;
mm2.print();
MM mm3=++mm2;
mm3.print();
return 0;
}

<font style="background-color:#FBDE28;">C++</font>
cout
本质是一个类的对象:ostream
,cin本质也是一个类的对象:istream
- 流重载必须要用友元方式重载
- 流重载尽量使用引用类型
class MM
{
public:
MM(string name="",int money=0):name(name),money(money){}
void print()
{
cout<<this->money<<"\t"<<this->name<<endl;
}
friend ostream& operator<<(ostream& out,MM& object);
friend istream& operator>>(istream& in,MM& object);
protected:
string name;
int money;
};
ostream& operator<<(ostream& out,MM& object)
{
out<<object.name<<"\t"<<object.money<<endl;
return out;
}
istream& operator>>(istream& in,MM& object)
{
cout<<"请输入info:"<<endl;
in>>object.name>>object.money;
return in;
}
int main()
{
MM mm1("king",100);
cin>>mm1;
cout<<mm1<<endl;
return 0;
}

<font style="background-color:#FBDE28;">C++</font>
- 所谓文本重载,就是重载后缀,固定写法
- 函数参数:
unsigned long long
(一定是) - 函数名:
operator""
要重载的后缀 —— 一般重载后缀采用下划线系列 - 一个运算符或者一个后缀只能重载被重载一次
本例中重载时间中的时分秒,用起来非常简便
unsigned long long operator"" _h(unsigned long long num)
{
return num*3600;
}
unsigned long long operator"" _min(unsigned long long num)
{
return num*60;
}
unsigned long long operator"" _s(unsigned long long num)
{
return num;
}
int main()
{
cout<<1_h+3_min+2_s<<endl;
return 0;
}

<font style="background-color:#FBDE28;">C++operator</font>
- 所谓隐式转换就是可以让对象直接赋值给普通对象——便捷获取数据成员的接口
- 模板
operator 隐式转换的类型()
{
return 数据;
}
class MM
{
public:
MM(string name="",int money=0):name(name),money(money){}
void print()
{
cout<<this->money<<"\t"<<this->name<<endl;
}
//隐式转换----->便捷获取数据成员的接口
operator int()
{
return this->money;
}
operator string()
{
return this->name;
}
protected:
string name;
int money;
};
int main()
{
MM mm("king",89);
int money=mm;
string name=mm;
cout<<money<<"\t"<<name<<endl;
return 0;
}

运算符重载的应用场景
- 让对象模拟指针的行为
- 模板
string king="King word!";
string::iterator iter;
for(iter=king.begin();iter!=king.end();iter++)
{
cout<<*iter<<" ";
}
cout<<endl;
struct Node
{
int data;
Node* next;
Node(int data=0):data(data),next(nullptr){}
};
class List
{
public:
void push_front(int data)
{
Node* newNode=new Node(data);
if(curSize==0)
tailNode=newNode;
else
newNode->next=frontNode;
frontNode=newNode;
curSize++;
}
class iterator
{
public:
iterator(Node* pmove=nullptr):pmove(pmove){}
iterator operator++(int)
{
return iterator(pmove=pmove->next);
}
bool operator!=(iterator&& object) const
{
return this->pmove!=object.pmove;
}
int operator*()
{
return pmove->data;
}
protected:
Node* pmove;
};
protected:
Node* frontNode=nullptr;
Node* tailNode=nullptr;
int curSize=0;
public:
iterator begin()
{
return iterator(frontNode);
}
iterator end()
{
return iterator(nullptr);
}
};
int main()
{
List list;
for(int i=0;i<=10;i++)
{
list.push_front(i+1);
}
List::iterator iter;
for(iter=list.begin();iter!=list.end();iter++)
{
cout<<*iter<<" ";
}
return 0;
}
- 把函数指针包装成一个对象
class Func
{
using Fun=void(*)();
public:
Func(Fun pf):pf(pf){}
void operator()()
{
pf();
}
protected:
Fun pf;
};
void print()
{
cout<<"我是函数包装器"<<endl;
}
int main()
{
Func x(print);
x(); //通过对象调用函数
return 0;
}
- 可以实现内存的自动申请与释放
class King
{
public:
King(int* ptr):ptr(ptr){}
~King()
{
if(ptr!=nullptr)
{
delete ptr;
ptr=nullptr;
}
}
int* operator->()
{
return this->ptr;
}
int operator*()
{
return *this->ptr;
}
protected:
int *ptr;
};
int main()
{
King k(new int(9999));
cout<<*k<<endl; //通过对象访问数据
return 0;
}