首页 > 代码库 > 再看 运算符重载
再看 运算符重载
运算符重载:分为 全局函数重载 和 成员函数重载两种:
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;这两种格式都要支持,那么用全局函数重载好;
记住 << >>必须是全局函数重载
记住 = [] () ->必须是成员函数重载
其余的都看具体情况
再看 运算符重载