您现在的位置是:首页 >技术杂谈 >运算符重载(重构)网站首页技术杂谈

运算符重载(重构)

涅槃豆 2024-07-11 18:01:02
简介运算符重载(重构)

自增运算符重载

之前我们了解了如何实现对两个复数对象实现相加操作,而我们熟知的运算符比如+=,++i,i++等操作暂时还没能正常使用

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

class Complex {
public:
	Complex(const double& dreal, const double& dimage)
		:_dreal(dreal)
		, _dimage(dimage)
	{
		cout << "Complex(double,double)" << endl;
	}
	void print()
	{
		cout << "F(x) = " << _dreal << " + " << _dimage << 'i' << endl;
	}
	Complex(const Complex& c)
		:_dreal(c._dreal)
		, _dimage(c._dimage)
	{
		;
	}
	~Complex()
	{
		cout << "~Complex" << endl;
	}
	friend Complex operator + (const Complex& lhs, const Complex& rhs);
private:
	double _dreal;
	double _dimage;
};

Complex operator + (const Complex& lhs, const Complex& rhs)
{
	Complex temp(lhs._dreal + rhs._dreal, lhs._dimage + rhs._dimage);
	return temp;
}
void test0()
{
	Complex c1(1, 2);
	Complex c2(3, 4);

	Complex c3 = c1 + c2;
	
	c3 += c1;//error
	++c3;//error
	c3++;//error
}
int main()
{
	test0();
	return 0;
}

重构+=操作

class Complex{
public:
	Complex& operator += (const Complex& rhs)
	{
		this->_dreal += rhs._dreal;
		this->_dimage += rhs._dimage;

		return *this;
	}
private:
	double _dreal;
	double _dimage;	
};

重构++i操作

class Complex{
public:
	Complex operator ++()
	{
		_dreal++;
		_dimage++;
	
		return *this;
	}
private:
	double _dreal;
	double _dimage;
};

重构i++操作

class Complex{
public:
	Complex operator ++(int)//int仅仅是用来标识重构的是i++操作,int没有任何实际意义
	{
		Complex temp(*this);
		
		++_dreal;
		++_dimage;
		return temp;//还原i++操作,i++为先反应后自增
	}
private:
	double _dreal;
	double _dimage;
};

上述操作中有一些函数定义中+=和++i带有引用符号,而i++则没有带,原因是i++返回的是临时变量,为右值无法进行取地址操作,而前两个返回的是this指针

由此也看出++i操作比i++操作要简便不少

总实现代码

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

class Complex {
public:
	Complex(const double& dreal, const double& dimage)
		:_dreal(dreal)
		, _dimage(dimage)
	{
		cout << "Complex(double,double)" << endl;
	}
	void print() const
	{
		cout << "F(x) = " << _dreal << " + " << _dimage << 'i' << endl;
	}
	Complex(const Complex& c)
		:_dreal(c._dreal)
		, _dimage(c._dimage)
	{
		cout << "Complex(const Complex&)" << endl;
	}
	~Complex()
	{
		cout << "~Complex" << endl;
	}
	friend Complex operator + (const Complex& lhs, const Complex& rhs);

	Complex& operator += (const Complex& rhs)
	{
		this->_dreal += rhs._dreal;
		this->_dimage += rhs._dimage;

		return *this;
	}

	Complex& operator ++()
	{
		_dreal++;
		_dimage++;

		return *this;
	}

	Complex operator ++(int)//int仅仅是用来标识重构的是i++操作,int没有任何实际意义
	{
		Complex temp(*this);

		++_dreal;
		++_dimage;
		return temp;//还原i++操作,i++为先反应后自增
	}
private:
	double _dreal;
	double _dimage;
};

Complex operator + (const Complex& lhs, const Complex& rhs)
{
	Complex temp(lhs._dreal + rhs._dreal, lhs._dimage + rhs._dimage);
	return temp;
}
void test0()
{
	Complex c1(1, 2);
	Complex c2(3, 4);

	c1.print();
	c2.print();

	Complex c3 = c1 + c2;
	c3.print();

	c3 += c1;
	c3.print();

	(++c3).print();
	(c3++).print();
	c3.print();
}
int main()
{
	test0();
	return 0;
}
Complex(double,double)
Complex(double,double)
F(x) = 1 + 2i
F(x) = 3 + 4i
Complex(double,double)
F(x) = 4 + 6i
F(x) = 5 + 8i
F(x) = 6 + 9i
Complex(const Complex&)
F(x) = 6 + 9i
~Complex
F(x) = 7 + 10i
~Complex
~Complex
~Complex

输入输出重载运算符

重构输入输出的操作就在于他们的输入输出运算符>>,<<

输出运算符重载

首先我们先对print成员函数优化一下,使他复数的表现形式更加标准规范

#include <iostream>

using std::endl;
using std::cout;

class Complex {
public:
	Complex(const double& dreal, const double& dimage)
		:_dreal(dreal)
		, _dimage(dimage)
	{
		cout << "Complex(double,double)" << endl;
	}
	~Complex()
	{
		cout << "~Complex" << endl;
	}
	void print() const
	{
		cout << "f(x) = ";
		if (!_dreal && !_dimage) cout << 0;

		if (_dreal)
		{
			cout << _dreal;
			if (_dimage > 0) cout << "+";
		}
		if (_dimage)
		{
			if (_dimage == 1) cout << "i";
			else if (_dimage == -1) cout << "-i";
			else cout << _dimage << 'i';
		}
		cout << endl;

	}
private:
	double _dreal;
	double _dimage;
};

void test1()
{
	Complex c1(1, 2)
		, c2(1, 0)
		, c3(1, -2)
		, c4(-1, 2)
		, c5(-1, 0)
		, c6(-1, -2)
		, c7(0, 2)
		, c8(0, 0)
		, c9(0, -2);

	c1.print();
	c2.print();
	c3.print();
	c4.print();
	c5.print();
	c6.print();
	c7.print();
	c8.print();
	c9.print();
}
int main()
{
	test1();
	return 0;
}

可以发现在我们输出一连串对象的时候会频繁调用print函数(因为调用需要一个.所以用起来非常难受)

<< 也是一种运算符,我们可以利用重构<<的方法省去调用函数的操作,观察可得,重构<<运算符需要两个参数,类似于cout的输出函数和其右边相应的值
那么我们就可以得到重构<<的运算符重载声明
ostream& operator <<(ostream& os,const Complex& rhs)

class Complex{
public:
	//在类中定义时的写法
	//std::ostream& operator <<(std::ostream& os)
	//该写法将右边的参数当做隐藏参数放在了第一个参数的位置上,违反了操作数的左右位置,于是我们更希望使用friend友元的方式完整的写出参数列表
	friend std::ostream& operator <<(std::ostream& os,const Complex& rhs);
private:
	double _dreal;
	double _dimage;
};
std::ostream& operator <<(std::ostream& os,const Complex& rhs)
{
	//先把输出的值放在os流中(单独一个cout)
	os << "f(x) = ";
	if (!rhs._dreal && !rhs._dimage) os << 0;

	if (rhs._dreal)
	{
		os << rhs._dreal;
		if (rhs._dimage > 0) os << "+";
	}
	if (rhs._dimage)
	{
		if (rhs._dimage == 1) os << "i";
		else if (rhs._dimage == -1) os << "-i";
		else os << rhs._dimage << 'i';
	}
	os << endl;

	//将os流中的数据全数输出
	return os;
}

优化后代码

#include <iostream>

using std::endl;
using std::cout;

class Complex {
public:
	Complex(const double& dreal, const double& dimage)
		:_dreal(dreal)
		, _dimage(dimage)
	{
		cout << "Complex(double,double)" << endl;
	}
	~Complex()
	{
		cout << "~Complex" << endl;
	}
	friend std::ostream& operator <<(std::ostream& os, const Complex& rhs);
private:
	double _dreal;
	double _dimage;
};

std::ostream& operator <<(std::ostream& os, const Complex& rhs)
{
	//先把输出的值放在os流中(单独一个cout)
	os << "f(x) = ";
	if (!rhs._dreal && !rhs._dimage) os << 0;

	if (rhs._dreal)
	{
		os << rhs._dreal;
		if (rhs._dimage > 0) os << "+";
	}
	if (rhs._dimage)
	{
		if (rhs._dimage == 1) os << "i";
		else if (rhs._dimage == -1) os << "-i";
		else os << rhs._dimage << 'i';
	}
	os << endl;

	//将os流中的数据全数输出
	return os;
}

void test1()
{
	Complex c1(1, 2)
		, c2(1, 0)
		, c3(1, -2)
		, c4(-1, 2)
		, c5(-1, 0)
		, c6(-1, -2)
		, c7(0, 2)
		, c8(0, 0)
		, c9(0, -2);

	cout << c1 << c2 << c3 << c4 << c5 <<c6 << c7 << c8 << c9 << endl;
}
int main()
{
	test1();
	return 0;
}
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
Complex(double,double)
f(x) = 1+2i
f(x) = 1
f(x) = 1-2i
f(x) = -1+2i
f(x) = -1
f(x) = -1-2i
f(x) = 2i
f(x) = 0
f(x) = -2i

~Complex
~Complex
~Complex
~Complex
~Complex
~Complex
~Complex
~Complex
~Complex

输入运算符重载

实际上就是对对象内的私有成员输入,重构格式与输出类似

class Complex{
public:
	friend std::istream& operator >>(std::istream& is,Complex& rhs);//不能带const因为对象的值需要修改
private:
	double _dreal;
	double _dimage;
};

void readDouble(std::istream& is,double& number)
{
	while(is>>number,!is.eof())
	{
		if(is.bad())
		{
			cout<<"istream has broken"<<endl;
			return;
		}
		if(is.fail())
		{
			is.clear();
			//第一个参数为流的最大输入字节数
			is.ignore(std::numeric_limits<std::streamsize>::max(),'
');
			cout<<"please input a interger number"<<endl;
		}
		else
		{
			cout<<"number:"<<number<<endl;
			return; 
		}
	}
}
std::istream& operator >>(std::istream& is.Complex& rhs)
{
	readDouble(rhs._dreal);
	readDouble(rhs._dimage);
}

代码

#include <iostream>

using std::endl;
using std::cout;

class Complex {
public:
	Complex()
	:_dreal(0)
	,_dimage(0)
	{
		cout<<"Complex()"<<endl;
	}
	Complex(const double& dreal, const double& dimage)
		:_dreal(dreal)
		, _dimage(dimage)
	{
		cout << "Complex(double,double)" << endl;
	}
	~Complex()
	{
		cout << "~Complex" << endl;
	}
	friend std::istream& operator >>(std::istream& is, Complex& rhs);
private:
	double _dreal;
	double _dimage;
};

void readDouble(std::istream& is, double& number)
{
	while (is >> number, !is.eof())
	{
		if (is.bad())
		{
			cout << "istream has broken" << endl;
			return;
		}
		if (is.fail())
		{
			is.clear();
			//第一个参数为流的最大输入字节数
			is.ignore(std::numeric_limits<std::streamsize>::max(), '
');
			cout << "please input a interger number" << endl;
		}
		else
		{
			cout << "number:" << number << endl;
			return;
		}
	}
}
std::istream& operator >>(std::istream& is,Complex& rhs)
{
	readDouble(is,rhs._dreal);
	readDouble(is,rhs._dimage);
	
	return is;
}

void test1()
{
	Complex c1;

	std::cin>>c1;

}
int main()
{
	test1();
	return 0;
}

函数调用重载

函数调用重载是一种比较特殊的函数重载,之前的函数重载是对同一名称的函数以不同参数为根据重载,函数调用重载在此基础上,通过对象来进行调用

#include <iostream>

using std::cout;
using std::endl;

class Func {
public:
	Func()
		:count(0)
	{
		;
	}
	int operator()(int x, int y)
	{
		++count;
		return x + y;
	}
	int operator()(int x, int y, int z)
	{
		++count;
		return x + y + z;
	}
private:
	int count;
};

int add(int x, int y)
{
	return x + y;
}
int add(int x, int y, int z)
{
	return x + y + z;
}
void test0()
{
	Func fun;

	//Func两种调用方式
	cout << fun(1, 2) << endl;
	cout << fun(1, 2, 3) << endl;
	cout << fun.operator()(1, 2) << endl;
	cout << fun.operator()(1, 2, 3) << endl;

	//普通函数调用以及重载
	cout << add(1, 2) << endl;
	cout << add(1, 2, 3) << endl;

	//指针调用
	typedef int (*aptr)(int, int);
	aptr addptr = add;
	cout << addptr(1, 2) << endl;
}
int main()
{
	test0();
}

下标访问运算符重载

在我们采用string类创建对象时,我们发现string对象可以直接用下标访问对应的字符,并且可以进行修改,接下来我们可以尝试模仿string类的这一特点来重载下表访问运算符

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

class CharArray{
public:
	CharArray(size_t& sz)
	:_data(new char[sz]())
	,_sz(sz)
	{
		;
	}
	int sz()
	{
		return _sz;
	}
	void release()
	{
		delete [] _data;
		_data = nullptr;
	}
	~CharArray()
	{
		if(_data) release();
		cout<<"~Complex"<<endl;
	}
private:
	size_t _sz;
	char* _data;
};
void test0()
{
	const char* pstr = "Hello World";
	
	CharArray(strlen(pstr) + 1);
	
	for(int i = 0;i < ca.sz();++i)
	{
		ca[i] = pstr[i];
	}
}
int main()
{
	test0();
}

此时报错显示 没有与这些操作数匹配的 “[]” 运算符

所以我们还需要重构[]运算符

class CharArray{
public:
		char& operator [](size_t idx)
	{
		cout << "char& operator []" << endl;
		if (idx > _sz)//因为类型为size_t,不会有负数的情况
		{
			cout << "Beyond the Array!" << endl;
			
			static char charnull = '';
			return  charnull;//因为返回的临时对象,加上static修饰使其在全局区,让charnull生命周期比函数更大
		}
		else
		{
			return _data[idx];
		}
	}
private:
	size_t _sz;
	char* _data;
};

此时我们还想模仿string,使得我们的CharArray对象能够通过cout直接输出

class CharArray{
public:
	friend std::ostream& operator <<(std::ostream& os,const CharArray& ca);
private:
	char* _data;
	size_t _sz;
};

std::ostream& operator <<(std::ostream& os,const CharArray& ca)
{
	for(size_t i = 0;i < ca.sz();++i)
	{
		os<<ca[i];
	}
	return os;
}

代码

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

class CharArray {
public:
	CharArray(const size_t& sz)
		:_data(new char[sz]())
		, _sz(sz)
	{
		;
	}
	size_t sz() const
	{
		return _sz;
	}
	void release()
	{
		delete[] _data;
		_data = nullptr;
	}
	~CharArray()
	{
		if (_data) release();
		cout << "~Complex" << endl;
	}

	char& operator [] (size_t& idx)
	{
		cout << "char& operator []" << endl;
		if (idx > _sz)//因为类型为size_t,不会有负数的情况
		{
			cout << "Beyond the Array!" << endl;

			static char charnull = '';
			return  charnull;
			//因为返回的临时对象,加上static修饰使其在全局区,让charnull生命周期比函数更大
		}
		else
		{
			return _data[idx];
		}
	}
	friend std::ostream& operator <<(std::ostream& os, CharArray& ca);
private:
	size_t _sz;
	char* _data;
};

std::ostream& operator <<(std::ostream& os, CharArray& ca)
{
	for (size_t i = 0; i < ca.sz(); ++i)
	{
		os << ca[i] <<' ';
	}
	return os;
}
void test0()
{
	const char* pstr = "Hello World";

	CharArray ca(strlen(pstr) + 1);

	for (size_t i = 0; i < ca.sz(); ++i)
	{
		ca[i] = pstr[i];
	}

	cout << ca << endl;
}
int main()
{
	test0();
}

<<重载时对象不能带const的原因

在函数过程中,如果发生了数组越界,那么我们之前所构建的下标重构中会返回charnull,虽然charnull是全局区,但因为引用符号实际上对ca的某个值发生了赋值操作,修改了对象,const不允许这样的操作出现,因此带上const发生了报错

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。