再看 运算符重载

来源:互联网 发布:16年淘宝食品类目新规 编辑:程序博客网 时间:2024/06/06 19:17

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

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: //注意这里为privateint 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_pSpacereturn *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;这两种格式都要支持,那么用全局函数重载好;

 

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

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

其余的都看具体情况

 

 

1 0
原创粉丝点击