首页 > 代码库 > 再看 运算符重载

再看 运算符重载

运算符重载:分为 全局函数重载 和 成员函数重载两种:

1:重载输入输出操作符:

第一版:全局函数重载:

// 运算符重载3.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <iostream>#include <ostream>using namespace std;class Test{public:	int i;	int j;	Test()	{		i = j = 0;	}	Test(int a, int b)	{		i = a;		j = b;	}};ostream& operator <<(ostream& out,const Test& t){	out<<t.i<<" "<<t.j<<endl;	return out;}istream& operator >>(istream& in, Test& t){	in>>t.i>>t.j;	if (in.fail())	{		cout<<"error"<<endl;	}	return in;}int _tmain(int argc, _TCHAR* argv[]){	Test t0;	Test t1(3,4);	cout<<t0<<t1<<endl;	Test t3;	cin>>t3;	cout<<t3;	getchar();	return 0;}

第二版:友元全局函数重载:

// 运算符重载3.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <iostream>#include <ostream>using namespace std;class Test{private: //注意这里为private	int i;	int j;public:	Test()	{		i = j = 0;	}	Test(int a, int b)	{		i = a;		j = b;	}	//严格的来说,运算符重载跟友元函数没有任何关系,之所以要用友元函数,是因为这里的i跟j是private类型,我们编写<< and >>的重载	//本来应该是属于全局变量函数重载的方式,但是全局函数没有访问i和j的权限,所以这里就声明为友元函数,使得可以访问i和j	//看网上有人说:运算符重载分为:成员函数重载与友元函数重载,严格来说不可以这么说,应该分为全局函数重载与成员函数重载	friend ostream& operator<<(ostream& out,const Test& t);	friend istream& operator >>(istream& in, Test& t);	//istream& operator >>(istream& in, Test& t);//ERROR  参数过多};//<< 与 >>运算符重载不可以为成员函数重载,因为那样相当于会有三个参数(还有一个this)ostream& operator <<(ostream& out,const Test& t){	out<<t.i<<" "<<t.j<<endl;	return out;}istream& operator >>(istream& in, Test& t){	in>>t.i>>t.j;	if (in.fail())	{		cout<<"error"<<endl;	}	return in;}int _tmain(int argc, _TCHAR* argv[]){	Test t0;	Test t1(3,4);	cout<<t0<<t1<<endl;	Test t3;	cin>>t3;	cout<<t3;	getchar();	return 0;}


//严格的来说,运算符重载跟友元函数没有任何关系,之所以要用友元函数,是因为这里的i跟j是private类型,我们编写<< and >>的重载
 //本来应该是属于全局变量函数重载的方式,但是全局函数没有访问i和j的权限,所以这里就声明为友元函数,使得可以访问i和j
 //看网上有人说:运算符重载分为:成员函数重载与友元函数重载,严格来说不可以这么说,应该分为全局函数重载与成员函数重载

2:成员函数重载与全局函数重载的区别:

// 运算符重载.cpp : 定义控制台应用程序的入口点。#include "stdafx.h"#include <iostream>using namespace std;class CComplex{public:	double m_i;//	double m_j;//	CComplex()	{		m_i = m_j;	}	CComplex(double m_i, double m_j = 0)	{		this->m_i = m_i;		this->m_j = m_j;	}	//成员函数重载;在类的内部实现单目运算是无参函数	CComplex operator-()	{		CComplex t(this->m_i,this->m_j);		t.m_i *= -1;		t.m_j *= -1;		return t;	}	//类的内部实现双目运算符是1个参数,只要带入右值,左值用this代替	/*	CComplex operator+(const CComplex rhs)	{		CComplex t;		t.m_i = m_i + rhs.m_i;		t.m_j = m_j + rhs.m_j;		return t;	}	*/	//成员函数运算符重载*	const CComplex operator*(const CComplex& rhs) 	{		CComplex t;		t.m_i = this->m_i * rhs.m_i;		t.m_j = this->m_j * rhs.m_j;		return t;	}	void Print()	{		cout<<"输出:"<<m_i<<" "<<m_j<<endl;	}};/*//负号重载,并不是想改变里面参数的数据,比如i为3,并不是要把这里的i改为-3;只是在它前面加上负号;所以返回一个临时对象CComplex operator-(const CComplex& c)//如果在函数里面不会改变参数本身,那么就设置为const参数{	//如果返回的是一个临时对象,那么函数返回值就是返回对象,不能返回引用(临时对象在函数结束会销毁,引用成为了孤立的)	CComplex t = c;	t.m_i = t.m_i * (-1);	t.m_j = (-1) * t.m_j;	return t;}*/CComplex operator+( const CComplex& c1,  const CComplex& c2){	CComplex c;	c.m_i = c1.m_i + c2.m_i;	c.m_j = c1.m_j + c2.m_j;	return c;}//全局函数CComplex operator *(const CComplex& lhs,const CComplex& rhs){	CComplex t;	t.m_i = lhs.m_i * rhs.m_i;	t.m_j = lhs.m_j * rhs.m_j;	return t;}int _tmain(int argc, _TCHAR* argv[]){	/*	CComplex test(33,44);	(-test).Print();	//-test.Print(); ERROR 符号优先级的原因	CComplex test2;	test2 = -test;	test2.Print();	*/		/************************************************************************/	/*operator+                                                             */	/************************************************************************/	CComplex c1(2,3);	CComplex c2(3,4);	CComplex c3 = 1+c2;	c3.Print();		/************************************************************************/	/*operator*                                                             */	/************************************************************************/	CComplex a1(1,2);	CComplex a2(2,3);	CComplex a3 = a1 * a2;	CComplex a4 = a1.operator*(a2);	a4.Print();	a3.Print();	/************************************************************************/	/*operaotr*                                                             */	/************************************************************************/	CComplex a5 = a1 * 3;//3 会隐式转换,这里调用的是成员函数的operator*	a5.Print();	//CComplex a6 = 4*a1; //error C2677: 二进制“*”: 没有找到接受“CComplex”类型的全局运算符(或没有可接受的转换)	//a6.Print(); 	//如果有全局的operator*  则可以编译通过;	CComplex a6 = 4*a1;	a6.Print();	/************************************************************************/	/* operator* 要声明为CComplex的友元函数吗?不需要,全局函数就可以实现要求*/	/************************************************************************/	getchar();	return 0;}


3:前置++与后置++的操作符重载

// 运算符重载2.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <iostream>using namespace std;class CComplex{public:	CComplex()	{		m_i = m_j = 0;	}	CComplex(int a, int b = 0)	{		m_j = b;		m_i = a;	}	void Print()	{		cout<<"m_i = "<<m_i<<" "<<"m_j = "<<m_j<<endl;	}	/*	CComplex operator ++(int)	{		CComplex ret(*this);		this->m_j++;		this->m_i++;		return ret;	}	CComplex& operator ++()	{		++m_i;		++m_j;		return *this;	}	*/public:	int m_i;	int m_j;};//前置++CComplex& operator++(CComplex& c) //1:返回的是参数本身,所以函数返回对象的引用,2:因为在函数当真会修改参数的值,所以参数不是const{	++c.m_i;	++c.m_j;		return c;}//后置++CComplex operator++(CComplex& c,int)//1:这里返回一个临时变量,所以返回值只能是对象,不能是引用;2:因为在函数当真会修改参数的值,所以参数不是const{	CComplex ret = c;	++c.m_i;	++c.m_j;	return ret;}int _tmain(int argc, _TCHAR* argv[]){	CComplex c1(1,3);	++c1;	c1.Print();	CComplex c2(1,3);	CComplex c3 = c2++;	c3.Print();	getchar();	return 0;}


前置++与后置++通过一个占位参数来区别

 

4:重载赋值操作符以及为什么复制操作符必须重载为成员函数

// 运算符重载4.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <iostream>using namespace std;class MyCArry{private:	int  m_iLength;	int* m_pSpace;public:	MyCArry()	{		m_iLength = 0;		m_pSpace  = NULL;	}	MyCArry(int length)	{		if (length < 0)		{			length = 0;		}		m_iLength = length;		m_pSpace = new int(length);			}	MyCArry(const MyCArry& test)	{		m_iLength = test.m_iLength;		m_pSpace  = new int(m_iLength);		for(int i = 0; i < m_iLength; i++)  	    {  			   m_pSpace[i] = test.m_pSpace[i];  		}  	}	/*	MyCArry& operator = (const MyCArry& rhs)	{		//注意要有自赋值检查		if ( this == &rhs)		{			*this = rhs;		}		this->m_iLength = rhs.m_iLength;		delete m_pSpace;		m_pSpace = new int(m_iLength);		for(int i = 0; i < m_iLength; i++)  		{  			m_pSpace[i] = rhs.m_pSpace[i];  		}  		return *this;	}	*/	//改进版本防止出现异常的时候内存泄漏	MyCArry& operator = (const MyCArry& rhs)	{		int* pOrig = m_pSpace;//记住原先的m_Space;		this->m_iLength = rhs.m_iLength;		m_pSpace = new int(m_iLength);		for(int i = 0; i < m_iLength; i++)  		{  			m_pSpace[i] = rhs.m_pSpace[i];  		}  		delete pOrig;//删除原先的m_pSpace		return *this;	}};//MyCArry& operator = (const MyCArry& rhs)  这里会报错误:operator = 必须是成员函数//对于赋值操作符(=)--比较特别,因为任何类如果不提供显示的拷贝赋值(即重载=),则编译器会隐式地提供一个。//这样的话,如果你再通过友元声明,进行全局的定义会造成调用二义性(即使允许,编译也会出错)。//=,[],(),->  只能声明为成员函数,是为了避免不合法的书写编译通过(推测。。。。。)(出现1=;1[],1(),1->;等这些格式)//其实这里+ ,+=等操作符,为类成员函数的时候: 1+a;编译不通过;为全局成员函数的时候:1+a;编译通过//所以这里不用纠结为什么= [] () ->不能重载为全局函数,就是C++的规定,至于为什么,实在是找不到原因,找到的也都没有说服力int _tmain(int argc, _TCHAR* argv[]){	MyCArry t1(5);	MyCArry t2;	t2 = t1;	return 0;}/*在实际开发过程中,单目运算符建议重载为成员函数,而双目运算符建议重载为友元函数。通常下双目运算符重载为友元函数比重载为成员函数更方便,但是有时双目运算符必须重载为成员函数,例如赋值运算符=。还有如果需要修改对象内部的状态,一般可以选择利用类成员函数进行修改。*/


//MyCArry& operator = (const MyCArry& rhs) 这里会报错误:operator = 必须是成员函数//对于赋值操作符(=)--比较特别,因为任何类如果不提供显示的拷贝赋值(即重载=),则编译器会隐式地提供一个。

//这样的话,如果你再通过友元声明,进行全局的定义会造成调用二义性(即使允许,编译也会出错)。

//=,[],(),-> 只能声明为成员函数,是为了避免不合法的书写编译通过(推测。。。。。)(出现1=;1[],1(),1->;等这些格式)

//其实这里+ ,+=等操作符,为类成员函数的时候: 1+a;编译不通过;为全局成员函数的时候:1+a;编译通过

//所以这里不用纠结为什么= [] () ->不能重载为全局函数,就是C++的规定,至于为什么,实在是找不到原因,找到的也都没有说服力

 

总结:

要选择哪种方式重载也没有一个确切的说法,要看具体情况,例如+,+=,是双目运算符,重载为成原函数可以避免出现 1 = a;1 +=a;这样的格式,但是*乘法具有

交换率,  1*a; a*1;这两种格式都要支持,那么用全局函数重载好;

 

记住 << >>必须是全局函数重载

记住 = [] () ->必须是成员函数重载

其余的都看具体情况

 

 

再看 运算符重载