-
运算符重载
对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据时导致不同的行为
-
运算符重载规则
(1)C++中的运算除少数几个之外,全部可以进行重载,而且只能重载C++中已经由的运算符;
(2)重载之后运算符的优先级和结合性都不会变;
(3)运算符重载时针对新类型数据的实际需要,对原有的运算符进行适当的改造。重载的功能应当与原有功能向类似,不能改变原运算符的员操作对象个数,同时至少有一个是自定义类型;
注:C++规定“.” “::” “?” “*”不能被重载
返回类型 operator 运算符(参数表) { 函数体 }//重载类的成员函数的一般语法; 返回类型 operator 运算符(形参表) { 函数体 }//重载类非成员函数,可以标注为友元;
注:当运算符重载为类的成员函数时,参数的个数比原来的操作数少一个(自加自减除外);当运算符重载为非成员函数时,参数个数与原操作数相同。前者第一操作数会被作为函数调用的目的对象无需出现在参数表中,函数体可以直接访问第一个操作数的成员;而后者运算符所有操作必须显式通过参数传递。
-
-
运算符重载为成员函数
运算符重载实质上就是函数重载,重载为成员函数,就可以自由访问本类的数据成员。实际使用时,总是通过该类的某个对象来访问重载的运算符。
如果为双目运算符则左操作数必须为本类的对象,由this指针而右操作数可以由其他数据类型隐含转换,用参数表来传递;
oprd1 B oprd2其中前者为A类的对象,B为A中的成员函数,该函数只有一个形参,形参的类型时oprd2的类型(有时也可能由其他数据类型隐含转换而来)oprd1 B oprd2等价于oprd1.B(oprd2);
对于前置单目运算符,“U”和“-”,如果要重载为A类的成员函数,U oprd等价于oprd.U();
对于后置“++”和”- -“oprd++或者oprd- -运算符重载为A类的成员函数需要带有一个int类型的形参,oprd++等价于oprd.++(0);
#include <iostream> using namespace std; class Complex { //复数类定义 public: //外部接口 Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) { } //构造函数 Complex operator + (const Complex c2) const; //运算符+重载成员函数 Complex operator - (const Complex c2) const; //运算符-重载成员函数 void display() const; //输出复数 private: //私有数据成员 double real; //复数实部 double imag; //复数虚部 }; Complex Complex::operator + (const Complex c2) const { //重载运算符函数实现 return Complex(real + c2.real, imag + c2.imag); //创建一个临时无名对象作为返回值 } Complex Complex::operator - (const Complex c2) const { //重载运算符函数实现 return Complex(real - c2.real, imag - c2.imag); //创建一个临时无名对象作为返回值 } void Complex::display() const { cout << "(" << real << ", " << imag << ")" << endl; } int main() { //主函数 Complex c1(5, 4), c2(2, 10), c3; //定义复数类的对象 cout << "c1 = "; c1.display(); cout << "c2 = "; c2.display(); c3 = c1 - 1; //使用重载运算符完成复数减法 //c3=c1.operator -(1); cout << "c3 = c1 - c2 = "; c3.display(); c3 = c1 + c2; //使用重载运算符完成复数加法 cout << "c3 = c1 + c2 = "; c3.display(); c3 = c2 + 2.0; cout << "c3 = c1 + c2 = " ;c3.display(); c3 = 2 + c2; //运行错误 cout << "c3 = c1 + c2 = " ;c3.display(); //c3 = 2.0 + c2; //cout << "c3 = c1 + c2 = " ;c3.display(); //c3 = 2 + c2; //cout << "c3 = c1 + c2 = " ;c3.display(); return 0; } //8_2.cpp #include <iostream> using namespace std; class Clock { //时钟类声明定义 public: //外部接口 Clock(int hour = 0, int minute = 0, int second = 0); void showTime() const; Clock operator ++ (); //前置单目运算符重载 Clock operator ++ (int); //后置单目运算符重载,有无参数 private: //私有数据成员 int hour, minute, second; }; Clock::Clock(int hour/* = 0 */, int minute/* = 0 */, int second/* = 0 */) { //构造函数,带初始值的默认构造函数不加初始值 if (0 <= hour && hour < 24 && 0 <= minute && minute < 60 && 0 <= second && second < 60) { this->hour = hour; this->minute = minute; this->second = second; } else cout << "Time error!" << endl; } void Clock::showTime() const { //显示时间函数 cout << hour << ":" << minute << ":" << second << endl; } Clock Clock::operator ++ () { //前置单目运算符重载函数 second++; if (second >= 60) { second -= 60; minute++; if (minute >= 60) { minute -= 60; hour = (hour + 1) % 24; } } return *this; } Clock Clock::operator ++ (int) { //后置单目运算符重载 //注意形参表中的整型参数 Clock old = *this; ++(*this); //调用前置“++”运算符 return old; } int main() { Clock myClock(23, 59, 59); cout << "First time output: "; myClock.showTime(); cout << "Show myClock++++: "; (myClock++++).showTime(); (myClock).showTime(); cout << "Show ++++myClock: "; (++++myClock).showTime(); return 0; }
注:对于函数参数表中未使用的参数,C++允许不给出参数名
-
运算符重载为非成员函数
运算符也可以重载为非成员函数,运算所需要的操作数都需要通过函数的形参表来传递,在形参表中形参从左到右的顺序就是运算符操作数的顺序。如果需要访问运算符参数对象的私有成员,可以将该函数声明为类的友元函数(也可以不,在不需要访问保护或者私有成员时)。
对于双目运算符B,如果要实现 oprd1 B oprd2,其中oprd1 和 oprd2中只要有一个具有自定义类型,就可以将B重载为非成员函数,函数的形参为oprd1 和 oprd2。经过重载之后,表达式oprd1 B op-d2就相当于函数调用operator B(oprd1,oprd2)。 对于前置单目运算符U,如“一”(负号)等,如果要实现表达式U oprd,其中 oprd 具有自定义类型,就可以将U重载为非成员函数,函数的形参为oprd。经过重载之后,表达式 U oprd 相当于函数调用operator U(oprd)。 对于后置运算符十十和一一,如果要实现表达式oprd ++或oprd --,其中 oprd为自定义类型,那么运算符就可以重载为非成员函数。这时函数的形参有两个,一个是 oprd,另一个是int类型形参。第二个参数是用于与前置运算符函数相区别的。重载之后,表达式 oprd ++和 oprd--就相当于函数调用operator ++(oprd,0)和 operator -- (oprd,0)。
#include <iostream> using namespace std; class Complex { //复数类定义 public: //外部接口 Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) { } //构造函数 friend Complex operator + (const Complex &c1, const Complex &c2); //运算符+重载 friend Complex operator - (const Complex &c1, const Complex &c2); //运算符-重载 **friend ostream & operator << (ostream &out, const Complex &c);** ***//运算符<<重载*** private: //私有数据成员 double real; //复数实部 double imag; //复数虚部 }; Complex operator + (const Complex &c1, const Complex &c2) { //重载运算符函数实现 return Complex(c1.real + c2.real, c1.imag + c2.imag); } Complex operator - (const Complex &c1, const Complex &c2) { //重载运算符函数实现 return Complex(c1.real - c2.real, c1.imag - c2.imag); } ostream & operator << (ostream &out, const Complex &c) { //重载运算符函数实现 out << "(" << c.real << ", " << c.imag << ")"; return out; } int main() { //主函数 Complex c1(5, 4), c2(2, 10), c3; //定义复数类的对象 cout << "c1 = " << c1 << endl; cout << "c2 = " << c2 << endl; c3 = c1 - c2; //使用重载运算符完成复数减法 cout << "c3 = c1 - c2 = " << c3 << endl; c3 = c2 + 2.0; cout << "c3 = c1 + c2 = " << c3 << endl; c3 = c2 + 2; cout << "c3 = c1 + c2 = " << c3 << endl; c3 = 2.0 + c2; cout << "c3 = c1 + c2 = " << c3 << endl; c3 = 2 + c2; cout << "c3 = c1 + c2 = " << c3 << endl; return 0; }
注:<<操作数是ostream类型的引用,ostream是cout类型的一个基类
两种运算符重载方式的对比(必须使用非成员函数的时候):
(1)要重载的操作符的第一个操作数不是可以更改的类型,例如上例中“<<”运算符的第一个操作数的类型为ostream,是标准库的类型,无法向其中添加成员函数。 (2)以非成员函数形式重载,支持更灵活的类型转换。例如例 8-3 中,可以直接使用 5.0+cl,因为Complex的构造函数使得实数可以被隐含转换为Complex类型。这样 5.0+cl 就会以operator+(Complex(5.0),c1)的方式来执行,c1+5.0也一样,从而支持了实数和复数的相加,很方便也很直观;而以成员函数重载时,左操作数必须具有 Complex类型,不能是实数(因为调用成员函数的目的对象不会被隐含转换),只有右操作数可以是实数(因为右操作数是函数的参数,可以隐含转换)。