操作符重载包括两种形式类成员函数实现操作符重载和友元函数实现操作符重载
之所以是这两种,是因为只有类的成员函数和友元函数才能访问类的私有变量,而显然操作符重载时,需要操作类的成员数据,如果成员数据是私有的,那么要么重载方法是类的成员函数,要么是类的友元函数,要么专门写一堆操作成员函数来实现(非成员函数非友元函数的重载操作符函数)来访问类的私有变量。
a.类成员函数实现操作符重载
[cpp] view plain copy print ? class 类名 { public: 返回类型 operator 操作符(形参表); }; //类外定义格式 返回类型 类名::operator 操作符(形参表) { //函数体 }关系运算符由类的成员函数重载
[cpp] view plain copy print ? #include<iostream> #include<string> using namespace std; class Sale_item { int bookid; int price; public: Sale_item operator +(Sale_item a);//返回类型是Sale_item,重载+操作,根据实参不同,调用不同的+法函数,实参是两个Sale_item类类型,就调用该加法方法 void SetItem(int a,int b); }; Sale_item Sale_item::operator +(Sale_item a)//实现上述声明 { int temp=-1; if(a.bookid==this->bookid) temp=a.price+this->price; Sale_item s; s.bookid=a.bookid; s.price=temp; return s; } void Sale_item::SetItem(int a,int b) { bookid=a; price=b; } int main() { Sale_item s1,s2; s1.SetItem(1,1); s2.SetItem(1,2); s1=s1+s2; } 使用类成员函数进行操作符重载时只需要一个形参,因为除了这个形参成员函数 还有一个隐含的形参this,this形参限定为第一个操作数 ,所以+虽然是2数操作,在使用类成员函数重载时,只需要一个参数就行。
另外需要注意的是用this访问类成员是this->而不是this.
赋值(=),下标([ ]),调用(()),成员访问(->)的重载必须是用成员函数来重载,不然会发生编译错误
一般一元操作符返回的是引用&,二元操作符返回的是对象(如果二元操作也返回引用,那么应该返回谁的引用呢?)
赋值运算符的重载(写成友元会报错Sale_item& operator=(....)must be a non-static member!)
赋值运算必须返回对*this 的引用(c++Primer)
[cpp] view plain copy print ? #include<iostream> #include<string> using namespace std; class Sale_item { private: int bookid; string name; public: Sale_item(int i,string j):bookid(i),name(j){} Sale_item& operator =(Sale_item &b) { this->bookid+=b.bookid; return *this; } void out() { cout<<bookid<<endl; } }; int main() { Sale_item a(1,"luozixuan"); Sale_item b(8,"luozixuan"); a=b; a.out(); }
C++ 赋值操作符必须防止自身给自身赋值
我们知道,在C++类中如果出现指针类型的成员,则必须重载赋值操作符来防止浅拷贝问题,那么重载的赋值操作符的实现一般按照这样的步骤: step1:delete 原来的内存,释放 step2:new 一块合适的新内存,以存放新的内容 step3:把新内容copy到new出来的新内存中,完成拷贝,同时防止了浅拷贝问题 那么,到了这里如何解释“赋值操作符必须防止自身给自身赋值”,原因就在step1那里,假如自己给自己赋值,则在step1的时候,对象的内存就已经被delete掉了,那么在step3的时候,就无法完成拷贝,因为已经被delete掉了
[cpp] view plain copy print ? class A { public: A(); A(const A &rhs); A& operator=(const A &rhs) { //if (this == &rhs) return *this; delete m_ptr; //step1 m_ptr = new int[SIZE]; //step2 memcpy(m_ptr,rhs.m_ptr,SIZE); //step3 这里会出错,因为如果this==&rhs,即自身给自身赋值,那么,在step1时内存已经被delete了 //应该在step1前面加上 if (this == &rhs) return *this;来防止自身赋值导致内存被删除而无法拷贝 return *this; } private: int *m_ptr; };
函数调用操作符的重载
函数调用操作符是(),因此,此操作符的函数重载是operator()()。重载函数调用操作符的类对象称为函数对象或仿函数(functor),因为我们可以像使用函数名一样使用对象名。
[cpp] view plain copy print ? #include<iostream> using namespace std; class stuInfo { public: int operator ()(int length,int width) { return length*width; } } ; int main() { int a=3; int b=2; stuInfo stu; cout<<"a * b= "<<stu(a,b)<<endl;//使用的是对象名,而不是类名 } 当然,也可以将一个函数对象传递给另一个函数,就像传递任何其他对象一样 [cpp] view plain copy print ? #include<iostream> using namespace std; class stuInfo { public: int operator ()(int length,int width) { return length*width; } }; void getSum(stuInfo& stu) { int a=2; int b=3; cout<<"a * b = "<<stu(a,b)<<endl; } int main() { stuInfo stu; getSum(stu); return 0; }
函数下标重载
[cpp] view plain copy print ? #include<iostream> #include<vector> #include<string> using namespace std; class stuInfo { private: int stuId; string stuName; public: stuInfo(int i,string j):stuId(i),stuName(j){} friend ostream& operator <<(ostream& out,const stuInfo& stu);//一般重载为友元函数 }; ostream& operator <<(ostream& out,const stuInfo& stu) { out<<"student id: "<<stu.stuId<<"\nstudent name: "<<stu.stuName<<endl; return out; } class Team { private: vector<stuInfo> team;//私有,以重载下标操作符[]的方式来实现类似于vector<int>的下标访问 public: stuInfo operator [](const int index) { return team[index]; } void push(stuInfo stu) { team.push_back(stu); } }; int main() { Team team; team.push(stuInfo(1,"luo")); team.push(stuInfo(2,"zi")); team.push(stuInfo(3,"xuan")); cout<<team[2]; }
成员访问操作符->的重载
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=28662931&id=3498450
对象访问成员用“.”,指针访问成员用“->”
箭头(->)操作符,看起来很像二元操作符,左操作数是类对象,右操作数是类成员 函数语句为: a->b;(其中b可以是函数或者成员;) 步骤1. 如果a是指针,指向一个具有成员b的类对象,那么a->b返回a类型的成员b,至此,语句结束; (因为指针访问就是用->) 步骤2. 如果a是一个对象(对象必须重定义了“operator->”,否则报错)(因为对象去访问用的是.),那么就调用a的operator->()函数,返回值:如果是指针则继续执行步骤1,如果是对象则继续执行步骤2,直到最终走到指针结束。
[cpp] view plain copy print ? #include<iostream> using namespace std; class A { public: void Print() { cout<<"this is A!"<<endl; } }; class B { private: A a; public: void Print() { cout<<"this is B!"<<endl; } A* operator ->() { return &a; } }; class C { private: B b; public: void Print() { cout<<"this is C!"<<endl; } B* operator ->() { return &b; } }; class D { private: B b; public: void Print() { cout<<"this is D!"<<endl; } B& operator ->() { return b; } }; int main() { C c; c->Print();//this is B! c.operator->()->Print();//this is B! c->operator->()->Print();//this is A! D d; d->Print();//this is A! return 0; } c->Print():c是对象,->操作访问重载->函数,返回了b,b是指针,访问其成员函数Print();
c.operator->()->Print():c是对象,.访问其成员函数->operator,返回了b,b是指针,访问其成员函数Print(); c->operator->()->Print():c是对象,(((c->)operator->())->Print())c->,c是对象,->操作访问重载->函数,返回了b,b是指针访问其成员函数operator->(),返回了指针a,指针a访问其成员函数Print();
d->Print():d是对象,->操作访问重载->函数,返回了b,b是对象,->操作访问重载->函数,返回了a,a是指针,访问其成员函数Print();
b.友元函数实现操作符重载
[cpp] view plain copy print ? class 类名 { friend 返回类型 operator 操作符(形参表); }; //类外定义格式: 返回类型 operator操作符(参数表) { //函数体 } 算术操作符和关系操作符一般重载为非成员函数(若数据成员私有,一般情况下是重载为友元函数)
[cpp] view plain copy print ? #include<iostream> #include<string> using namespace std; class Sale_item { int bookid; int price; public: friend Sale_item operator +(Sale_item a,Sale_item b); void SetItem(int a,int b); }; Sale_item operator +(Sale_item a,Sale_item b) { int temp=-1; if(a.bookid==b.bookid) temp=a.price+b.price; Sale_item s; s.bookid=a.bookid; s.price=temp; return s; } void Sale_item::SetItem(int a,int b) { bookid=a; price=b; } int main() { Sale_item s1,s2; s1.SetItem(1,1); s2.SetItem(1,2); s1=s1+s2; } 友元函数没有this这个隐含的形参,所以传参时需要传两个
对于IO操作符<<,>>的重载,应当使用非成员函数(一般是友元函数)来重载。
为了与标准库IO操作一致,重载 << 操作符函数应把ostream&作为其第一个参数,对类类型const对象的引用作为第二个参数,并返回对ostream形参的引用 ,因为成员函数的第一形参限定为this,所以不能用成员函数来重载输出操作符。istream同理
[cpp] view plain copy print ? #include<iostream> #include<string> using namespace std; class Sale_item { private: int bookid; string name; public: Sale_item(){} friend ostream& operator <<(ostream& out,const Sale_item& a); friend istream& operator >>(istream& in,Sale_item& a); }; ostream& operator <<(ostream& out,const Sale_item& a)//对输出操作符的重载,应当定义为非成员函数,成员函数第一形参是this { out<<"book name: "<<a.name<<"\n"<<"book id: "<<a.bookid<<endl; return out; } istream& operator >>(istream& in,Sale_item& a)//形参in是istream对象的一个引用,因为istream不能拷贝(istream的拷贝构造函数是private的) { //返回类型是istream对象的引用,一是因为istream不能拷贝,所以必须返回引用,二是为了达到多个操作符>>操作一个istream对象的效果。即如果我返回的是一个istream的拷贝(临时对象),那么后续>>操作就会对这个返回的临时对象操作,而流是全局的,操作的也是全局的流,那么后续的>>操作就都无效了。 in>>a.bookid>>a.name; return in; } int main() { Sale_item a; cin>>a; cout<<a; } 函数返回引用如string &,那么它的参数也必须是引用或指针,不能是局部对象。
返回引用部产生对象拷贝,而是返回对象本身。