上一节中鸡啄米讲到了运算符重载的概念和规则,运算符可以重载为类的成员函数或友元函数,这一节就来讲讲运算符怎样重载为类的成员函数。
运算符重载为类的成员函数后就可以像其他成员函数一样访问本类的数据成员了。在类的外部通过类的对象,可以像原运算符的使用方式那样使用重载的运算符,比如,“+”运算符被重载为类A的成员函数后,A的对象a和其他对象b就可以这样进行加法运算:a+b。
如果是双目运算符,比如“+”和“-”,则一个操作数是使用此运算符的对象本身,另一个操作数使用运算符重载函数传递进来的对象。假设有双目运算符U,a为类A的对象,另有某类也可以是A类的对象b,我们想实现a U b这样的运算,就可以把U重载为类A的成员函数,此函数只有一个形参,形参的类型为对象b的类型。这样进行a U b的运算就相当于函数调用:a.operator U(b)。
如果是单目运算符,比如“++”和“--”,操作数就是此对象本身,重载函数不需要传递参数,只是后置单目运算符语法上规定有一个形式上的参数,以区别于前置单目运算符。
假设有前置单目运算符U,如前置“++”,a为类A的对象,我们想实现U a这样的运算,也可以把U重载为类A的成员函数,此函数没有形参。这样U a表达式就相当于函数调用:a.operator U()。
假设有后置单目运算符U,如后置“--”,a为类A的对象,我们想实现a U这样的运算,同样可以把U重载为类A的成员函数,但此函数需要有一个整型的形参。重载后a U表达式就相当于函数调用:a.operator U(0)。
这里鸡啄米需要强调下,前置单目运算符重载和后置单目运算符重载在语法形式上的区别就是前者重载函数没有形参,而后者重载函数有一个整型形参,此形参对函数体没有任何影响,这只是语法上的规定,仅仅是为了区分前置和后置。
鸡啄米给大家两个程序例子,分别演示双目运算符和单目运算符的使用。
第一个例子:时间值的加法,比如2个小时20分钟加3个小时30分钟,应该是5个小时50分钟,运算规则就是小时数相加,分钟数相加,如果分钟数的和超过60分钟则小时数再加1,分钟数减60。双目运算符“+”需要重载为时间值类的成员函数,此函数只有一个形参,类型也是时间值类的对象。
#include <iostream> using namespace std; class CTimeSpan { public: CTimeSpan(int nHours=0, int nMins=0); // 构造函数 CTimeSpan operator +(CTimeSpan ts); // 运算符“+”重载为成员函数 int GetHours() { return m_nHours; } // 获取小时数 int GetMins() { return m_nMins; } // 获取分钟数 void Show(); // 显示时间值 private: int m_nHours; // 小时数 int m_nMins; // 分钟数 }; CTimeSpan::CTimeSpan(int nHours, int nMins) // 构造函数的实现 { nHours += nMins/60; nMins %= 60; m_nHours = nHours; m_nMins = nMins; } CTimeSpan CTimeSpan::operator +(CTimeSpan ts) // 重载运算符函数实现 { int nNewHours; int nNewMins; nNewHours = m_nHours + ts.GetHours(); nNewMins = m_nMins + ts.GetMins(); nNewHours += nNewMins/60; nNewMins %= 60; return CTimeSpan(nNewHours, nNewMins); } void CTimeSpan::Show() { cout << m_nHours << "小时" << m_nMins << "分钟" << endl; } int main() { CTimeSpan timeSpan1(2, 50); CTimeSpan timeSpan2(3, 30); CTimeSpan timeSum; timeSum = timeSpan1 + timeSpan2; cout << "timeSpan1: "; timeSpan1.Show(); cout << "timeSpan2: "; timeSpan2.Show(); timeSum = timeSpan1 + timeSpan2; cout << "timeSum=timeSpan1+timeSpan2: "; timeSum.Show(); return 0; }
程序运行结果:
timeSpan1: 2小时50分钟
timeSpan2: 3小时30分钟
timeSum=timeSpan1+timeSpan2: 6小时20分钟
我们可以看出,运算符重载成员函数跟一般的成员函数类似,只是使用了关键字operator。使用重载运算符的方式与原运算符相同。运算符作用于整型、浮点型和CTimeSpan等不同的对象会发生不同的操作行为,这就是多态性。
注意,重载“+”的函数中,语句return CTimeSpan(nNewHours, nNewMins);看似是对CTimeSpan构造函数的调用,实则不然,这是构造一个临时对象并将它返回到主函数中。
第二个例子:时钟类的例子。前置“++”和后置“++”重载为时钟类的成员函数。前置“++”重载函数没有形参,后置“++”重载函数有一个整型形参。
#include <iostream> using namespace std; class Clock //时钟类声明 { public: //外部接口 Clock(int NewH=0, int NewM=0, int NewS=0); void ShowTime(); Clock& operator ++(); //前置单目运算符重载 Clock operator ++(int); //后置单目运算符重载 private: //私有数据成员 int Hour,Minute,Second; }; Clock::Clock(int NewH, int NewM, int NewS) { if (0<=NewH && NewH<24 && 0<=NewM && NewM<60 && 0<= NewS && NewS<60) { Hour = NewH; Minute = NewM; Second = NewS; } else cout << "错误的时间!" << endl; } void Clock::ShowTime() { cout << Hour << ":" << Minute << ":" << Second << endl; } Clock& Clock::operator ++() //前置单目运算符重载函数 { Second++; if(Second>=60) { Second=Second-60; Minute++; if(Minute>=60) { Minute=Minute-60; Hour++; Hour=Hour%24; } } return *this; } //后置单目运算符重载 Clock Clock::operator ++(int) //注意形参表中的整型参数 { Clock old=*this; ++(*this); return old; } int main() { Clock myClock(23,59,59); cout<<"初始时间myClock:"; myClock.ShowTime(); cout<<"myClock++:"; (myClock++).ShowTime(); cout<<"++myClock:"; (++myClock).ShowTime(); return 0; }
程序运行结果:
初始时间myClock:23:59:59
myClock++:23:59:59
++myClock:0:0:1
因为后置单目运算符重载函数中的整型形参没有实际意义,只是为了区分前置和后置,所以参数表中只给出类型就行了,参数名写不写都可以。
运算符重载部分需要大家认真理解,可以在VS2010上运行上面两个例子,然后自己举一反三,写几个简单例子试验下其他运算符。
鸡啄米就讲到这了,有问题欢迎在鸡啄米博客交流。谢谢大家。