【⑤C++ | 运算符重载】意义 | 限制 | 方法 | 规则 | 特殊运算符重载 | 应用场景

在这里插入图片描述

前言

✨欢迎来到小KC++专栏,本节将为大家带来C++运算符重载 — 意义 | 限制 | 方法 | 规则 | 特殊运算符重载 | 应用场景 的分享


运算符重载,就是赋予运算符新的含义。它是重载系统内部运算符的函数,是实现类静态多态性的方式之一。它本质上是函数重载。
实际上,我们已经在不知不觉之中使用了运算符重载。列如:我们习惯使用的对整数,浮点数运用+、-、*、/,起始计算机对整数和浮点数的操作过程是不一样的,但由于C++已经对运算符进行了重载,所以才能都适用。
又如<<本来是左移运算符,但在输出操作中,与cout搭配,当作输出流运算符了

一、运算符重载的意义

我们平常常见的算术运算符、逻辑运算符等运算符都是系统库函数里面已经存在的,所以运算符可以直接用于我们常用的数据类型。然而对于我们自定义的类实例化的对象,系统没有与之相关的运算符可以操作,但是为了使我们编写程序时更加便捷,C++提供了一种方式——运算符重载,来满足我们对于类对象进行的操作。

也就是说运算符重载是为了解决类对象之间的运算的,让编译器在遇到对象运算时能按我们要求的进行运算,这就是运算符重载的意义。

二、运算符重载限制

C++中绝大部分的运算符允许重载,少部分不允许重载,详细描述如下

可以重载的运算符

  • 算术运算符:+ - * / %
  • 自增、自减运算符:++ –
  • 位运算符:| & ~ ^ << >>
  • 逻辑运算符:|| && !
  • 关系运算符:== != < > <= >=
  • 赋值运算符:= += -= /= %= &= |= ^= <<= >>=
  • 单目运算符:+ - * &
  • 动态内存分配:new delete new[] delete[]
  • 其他运算符:() -> , []

不能重载的运算符

  • . 成员访问运算符
  • :: 域运算符
  • .* ->* 成员指针访问运算符
  • szieof 长度运算符
  • ?: 条件运算符

三、运算符重载规则

  • 重载运算符函数可以对运算符作出新的解释﹐但原有基本语义不变:

    • 不改变运算符的优先级
    • 不改变运算符的结合性
    • 不改变运算符所需要的操作数
    • 不能创建新的运算符
  • 语法:

在这里插入图片描述

  • 一个运算符被重载后,原有意义没有失去,只是定义了相对一特定类的一个新运算符。
  • 函数返回值:当前运算符运算结束后产物类型,int a,int b,a+b返回int类型
  • 函数名:operator和运算符组成函数名
  • 函数参数
    • 运算符重载函数是类中成员函数:函数参数等于操作数-1
    • 运算符重载函数是友元函数:函数参数等于操作数
    • 函数体:写运算符的实际想要的运算
  • c++类中存在一个赋值的重载函数(默认)

四、运算符重载方法

两种重载方法

重载为成员函数全局(友元)函数

在这里插入图片描述

两种形式的选择时机:

  • 左操作数(或者只有左操作数并且)是本类的对象时,可选用成员函数形式。

在这里插入图片描述

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

在这里插入图片描述

  • 一般单目运算符最好被重载为成员函数;双目运算符重载为友元函数。

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

在这里插入图片描述

#include <iostream>
using namespace std;
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++特殊运算符重载

1. .= () -> [] 只能重载为类成员函数
2. 运算符重载必须存在至少一个自定义类型才能重载
3. . .* ?: :: 不能被重载
4. C++只允许重载已有运算符,不能无中生有
5. 习惯行为:单目运算符采用类的成员函数重载,双目运算符采用友元重载

1、++和–运算符的重载

对于++和–重载,通过增加无用参数(int)标识为后置运算,下面是一个代码示例和示例结果

#include<iostream>
#include<string>
using namespace std;
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;
}

在这里插入图片描述

2、C++流对象重载

  • cout本质是一个类的对象:ostream,cin本质也是一个类的对象:istream
  • 流重载必须要用友元方式重载
  • 流重载尽量使用引用类型
#include<iostream>
#include<string>
using namespace std;
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;
}

在这里插入图片描述

3、C++文本重载

  • 所谓文本重载,就是重载后缀,固定写法
  • 函数参数:unsigned long long(一定是)
  • 函数名:operator""要重载的后缀 —— 一般重载后缀采用下划线系列
  • 一个运算符或者一个后缀只能重载被重载一次

本例中重载时间中的时分秒,用起来非常简便

#include<iostream>
#include<string>
using namespace std;
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;
}

在这里插入图片描述

4、C++operator实现隐式转换

  • 所谓隐式转换就是可以让对象直接赋值给普通对象——便捷获取数据成员的接口

  • 模板

    operator 隐式转换的类型()
    {
        return 数据;
    }
    
#include<iostream>
#include<string>
using namespace std;
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;
}

在这里插入图片描述

六、运算符重载的应用场景

1、迭代器实现

  • 让对象模拟指针的行为

  • 模板

    #include<iostream>
    #include<string>
    using namespace std;
    int main()
    {
    	string king="King word!";
    	string::iterator iter;
    	for(iter=king.begin();iter!=king.end();iter++)
    	{
    		cout<<*iter<<" ";
    	}
    	cout<<endl;
    }
    
#include<iostream>
#include<string>
using namespace std;
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;
}

2、函数包装器

  • 把函数指针包装成一个对象
#include<iostream>
#include<string>
using namespace std;
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;
}

3、智能指针

  • 可以实现内存的自动申请与释放
#include<iostream>
#include<string>
using namespace std;

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;
}

总结

通过运算符重载,可以使自定义类型的操作更加直观和方便。但在进行运算符重载时,需要遵守一定的规则和限制,以确保代码的正确性和可维护性。

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热爱编程的张同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值