C++运算符重载一:运算符重载、复数类CComplex、输出运算符重载

帅比萌擦擦* 提交于 2020-03-11 13:15:03

一、运算符重载

运算符重载 使对象的运算运算表现得和编译器内置类型一样,使同一个运算符可以有不同的功能。即定义一个重载运算符的函数,使指定的运算符不仅能实现原有的功能,而且能实现在函数中指定的新的功能。

运算符重载实质上是函数的重载,重载运算符的函数一般格式如下
函数类型 operator 运算符名称(形参表)
{对运算符的重载处理}

operator是关键字,是专门用于定义重载运算符的函数的,运算符名就是C++已有的运算符。

运算符重载的规则:
(1)C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载。
(2)C++允许重载的运算符
不能重载重载的运算符只有5个:
① .   成员访问运算符
② *   成员指针访问运算符
③ ::   域运算符
④sizeof 长度运算符
⑤ ?:   条件运算符
(3)重载不能改变运算符运算对象(即操作数)的个数
(4)重载不能改变运算符的优先级别
(5)重载不能改变运算符的结核性
(6)重载不能改变默认的参数
(7)重载的运算符必须和用户自定义类型的对象一起使用,其参数至少有一个是类对象(或类对象的引用)
(8)用于类对象的运算符一般必须重载,但有两个例外,“=”和“&”不必用户重载

运算符重载的函数有两种处理方式:
1.把运算符重载的函数作为类的成员函数
如果运算符重载函数作为成员函数,它可以通过this指针自由地访问本类的数据成员,因此可以少写一个函数的参数。
2.运算符重载的函数不是类的成员函数(可以是一个普通函数),在类中把它声明为友元函数
将双目运算符重载为友元函数时,由于友元函数不是该类的成员函数,因此在函数的形参列表中必须有两个参数,不能省略。

注意:
1.运算符重载不仅可以给类的成员方法提供重载,还可以在全局提供重载。
2.编译器做对象运算的时候,会调用运算符的重载函数(优先调用成员方法);如果没有成员方法,就在全局作用域找合适的运算符重载函数。
为什么需要运算符重载?它有什么好处?我们来看一个简单的代码

template <typename T>
T sum(T a, T b)//a,b都没有指定的类型
{
	return a + b;//a.operator+(b)
}

这里的a,b都没有指定类型,如果我们使用的是编译器的内置类型,可以使用;但是如果T类型如果是对象类型,a与b则是两个对象,对象与对象的相加就要用到我们运算符重载。那么如何进行重载呢,我们以复数类CComplex为例进行运算符的重载。

二、复数类CComplex

1.普通的复数类CCpmplex中加法运算符重载
①先来看普通的复数类的运算符重载

//复数类
class CComplex
{
public:
	//CComplex() CComplex(20) CComplex(30,30)
	CComplex(int r = 0, int i = 0)//构造
		:mreal(r), mimage(i){}
	//加法运算符重载
	CComplex operator+(const CComplex &src)
	{
		CComplex comp;
		comp.mreal = this->mreal + src.mreal;
		comp.mimage = this->mimage + src.mimage;
		return comp;
		//return CComplex(this->mreal + src.mreal,
		//this->mimage + src.mimage);
	}
	void show()
	{
		cout << "real:" << mreal << " image" << mimage << endl;
	}
private:
	int mreal;//实部
	int mimage;//虚部
};

int main()
{
	CComplex comp1(10, 10);
	CComplex comp2(20, 20);
	//comp1.operator.+(comp2)加法运算符的重载函数
	CComplex comp3 = comp1 + comp2;
	comp3.show();

	CComplex comp4 = comp1 + 20;//将20加到comp1实部
	comp4.show();

	return 0;
}

输出结果:执行成功。
在这里插入图片描述
②那么我们输入如下代码,comp4可以执行成功吗?

CComplex comp4 = comp1 + 20;//将20加到comp1实部

输出结果:执行成功,comp4可以成功重载,comp4中comp1调用了中间加法运算符重载,将20当作实参传入:com1.operator+(20),形参类型为当前复数类型的引用变量,引用不了整数20。实参类型到形参类型强转,int->CComplex,CComplex(int);整型如何转为CComplex复数类下,编译器会找复数类型有没有带整型参数的构造函数来生成临时对象,成功找到。
在这里插入图片描述
③我们再来看看,comp5可以执行成功吗?

CComplex comp5 = 30 + comp1;

我们来分析一下:comp5不能重载成功。30并不需要类型转换,无法调用到成员方法中的运算符重载函数,所以执行失败。
那么如何解决这个问题呢?
我们在全局作用域再提供一个运算符重载函数:不需要对象来调用,除了运算符+,其他都当做实参传入。并在私有下声明其为友元函数。全局的运算符重载功能:1.对象与对象运算 2.对象与整数运算 3.整数与对象运算。

CComplex operator+(const CComplex &lhs, const CComplex &rhs)
{
	return CComplex(lhs.mreal + rhs.mreal, lhs.mimage + rhs.mimage);
}

输出结果:执行成功。
在这里插入图片描述
有了全局的重载函数,局部的可省略掉:

//复数类
class CComplex
{
public:
	CComplex(int r = 0, int i = 0)//构造
		:mreal(r), mimage(i){}
	void show()
	{
		cout << "real:" << mreal << " image" << mimage << endl;
	}
private:
	int mreal;//实部
	int mimage;//虚部
	friend CComplex operator+(const CComplex &lhs, const CComplex &rhs);//友元
};
CComplex operator+(const CComplex &lhs, const CComplex &rhs)
{
	return CComplex(lhs.mreal + rhs.mreal, lhs.mimage + rhs.mimage);
}

④++运算符的重载:
因为我们++或–是单目运算符,operator++这样写无法区别前置++与后置++
因此,为了区分它们:

  1. 前置++:operator++()
  2. 后置++:operator++(int)
//复数类
class CComplex
{
public:
	//CComplex() CComplex(20) CComplex(30,30)
	CComplex(int r = 0, int i = 0)//构造
		:mreal(r), mimage(i){}
	CComplex operator++(int)//后置++重载
	{
		//CComplex comp = *this;//保留旧值
		//mreal += 1;
		//mimage += 1;
		//return comp;//返回旧值
		return CComplex(mreal++, mimage++);
	}
	CComplex& operator++()//前置++重载
	{
		mreal += 1;
		mimage += 1;
		return *this;//返回加完后的值
	}
	void show()
	{
		cout << "real:" << mreal << " image" << mimage << endl;
	}
private:
	int mreal;//实部
	int mimage;//虚部
	friend CComplex operator+(const CComplex &lhs, const CComplex &rhs);//友元
};
CComplex operator+(const CComplex &lhs, const CComplex &rhs)
{
	return CComplex(lhs.mreal + rhs.mreal, lhs.mimage + rhs.mimage);
}

int main()
{
	CComplex comp1(10, 10);
	CComplex comp2(20, 20);

	comp5 = comp1++;
	comp1.show();
	comp5.show();

	comp5 = ++comp1;
	comp1.show();
	comp5.show();

	return 0;
}

输出结果:执行成功。
在这里插入图片描述
⑤复合运算符赋值重载:例如+=即为复合赋值运算符
还是接着CComplex来看:

//+=运算符重载
void operator+=(const CComplex &src)
{
	mreal += src.mreal;
	mimage += src.mimage;
}

执行结果:执行成功。
在这里插入图片描述

三、输出运算符重载

假如我们写了一个模板代码:里面接收一个对象或者变量,在show中进行打印。但是存在问题,并不知道用户将来会有什么类型为其实例化。万一用户使用自定义的类类型对其实例化,例如:CComplex,如何对CComplex对象打印?

template<typename T>
void show(T a)
{
	cout << a <<endl;
}

因此为了让对象的打印达到通用,我们需要对输出运算符进行重载,需要将其提供为全局方法并将其声明为友元函数。

ostream& operator<<(ostream &out, const CComplex &src)
{
	out << "mreal:" << src.mreal << "mimage:" << src.mimage << endl;
	return out;
}

执行结果:输出成功。
在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!