• 1. 《C++面向对象程序设计》教学内容 第1章 C++概述 第2章 类和对象 第3章 面向对象程序设计概述 第4章 进一步学习类和对象 第5章 堆与复制构造函数 第6章 继承性:派生类 第7章 运算符重载 第8章 虚函数和多态性 第9章 模板 第10章 类库和C++的标准模板库STL 第11章 输入输出流 第12章 异常处理
  • 2. 第7章 运算符重载7.1 重载运算符的目的 7.2 运算符重载语法 7.3 成员运算符函数 7.4 友元运算符函数 7.5 成员运算符函数与友元运算符函数比较 7.6 “++”和“--”的重载 7.7 赋值运算符“=”的重载 7.7 下标运算符”[ ]”与函数调用运算符”( )”的重载 7.9 构造函数用于类型转换 7.10 应用举例
  • 3. 7.1 重载运算符的目的简略的表达方法 函数式的表达方法 运算符函数 运算符重载
  • 4. 简略的表达方法x+y*z 简略的表达方法是一种缩略或公式化表达的方法。 好处:简明直观、能提高交流效率。
  • 5. 函数式的表达方法class Complex{ private: double real; double image; public: Complex( ); Complex& Add(Complex&); }; //… Complex object1,object2,object3; …… object3 =object1.Add(object2);这种表示复数加法很不直观
  • 6. 运算符函数class complex{ / / very simplified complex double re, im; public: complex(double r, double i) : re(r) , im(i) { } complex operator+(complex) ; complex operator*(complex) ; };运算符函数
  • 7. 运算符重载void f( ) { complex a = complex(1, 3.1) ; complex b = complex(1.2, 2) ; complex c = b; a = b+c; b = b+c*a; c = a*b+complex(1,2) ; }
  • 8. 7.2 运算符重载语法运算符重载 C++语言提供的标准运算符可以重新在类中定义,使标准运算符作用于类的对象,从而使对象的计算操作表示得既自然又符合常规。 运算符函数 C++将运算符看作一种特殊类型的函数,运算符的重载是通过对运算符函数的重载实现的。运算符函数名由关键字operator 和重载得运算符组成。
  • 9. 运算符重载的方法首先在类定义时说明待重载的运算符,其格式为: 返回值类型 operator op (参数表); 然后象定义函数一样定义重载运算符函数。 返回值类型 类名称∷operator op(参数表) { //…… 运算符处理程序代码 }
  • 10. 运算符重载的实质必要性 C++中预定义的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类型(如类) 实现机制 运算符重载的实质就是函数重载:每个运算符对应各自的运算符函数,根据操作数的不同调用不同的同名函数。 将指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参。
  • 11. 7.3 成员运算符函数重载为类成员函数时 参数个数=原操作数个数-1 (后置++、--除外) 当类的对象调用这种运算符函数时,对象中的成员数据就可以是一个操作数,另一个操作数才通过参数传递来获得。 如: class complex {public complex operator + (complex c2); ……. }
  • 12. #include class complex //复数类声明 { public: //外部接口 complex(double r=0.0,double i=0.0) {real=r;imag=i;} //构造函数 complex operator + (complex c2); //+重载为成员函数 complex operator - (complex c2); //-重为载成员函数 void display( ); //输出复数 private: //私有数据成员 double real; //复数实部 double imag; //复数虚部 }; 简单复数类的运算符重载
  • 13. //重载运算符函数的实现 complex complex::operator +(complex c2) { complex c; c.real=c2.real+real; c.imag=c2.imag+imag; return complex(c.real,c.imag); }简单复数类的运算符重载(续)
  • 14. //重载运算符 - 函数实现 complex complex::operator - (complex c2) { complex c; c.real=real-c2.real; c.imag=imag-c2.imag; return complex(c.real,c.imag); }简单复数类的运算符重载(续)
  • 15. void complex::display( ) { cout<<"("<
  • 16. 程序输出的结果为: c1=(5,4) c2=(2,10) c3=c1-c2=(3,-6) c3=c1+c2=(7,14) 注:交换c1和c2的位置,不影响结果。简单复数类的运算符重载(续)
  • 17. 7.4 友元运算符函数重载为友元函数时 参数个数=原操作数个数,且至少应该有一个自定义类型的形参。 友元函数是类以外的函数,调用这种运算符函数时,所有的操作数都要通过参数传递来获得。 如: class complex {public friend complex operator + (complex c1, complex c2); ……. }
  • 18. #include class complex //复数类声明 { public: //外部接口 complex(double r=0.0,double i=0.0) { real=r; imag=i; } //构造函数 friend complex operator + (complex c1,complex c2); //运算符+重载为友元函数 friend complex operator - (complex c1,complex c2); //运算符-重载为友元函数 void display(); //显示复数的值 private: //私有数据成员 double real; double imag; }; 复数类的友元运算符函数
  • 19. complex operator +(complex c1,complex c2) //运算符重载友元函数实现 { return complex(c2.real+c1.real, c2.imag+c1.imag); } complex operator -(complex c1,complex c2) //运算符重载友元函数实现 { return complex(c1.real-c2.real, c1.imag-c2.imag); } // 其它部分同上例复数类的友元运算符函数(续)
  • 20. 7.5 成员运算符函数与友元运算符函数比较C++的大部分运算符即可以说明为成员函数运算符,也可以说明成友元函数运算符。 究竟选择哪一种运算符函数好一些,没有定论,这主要取决于实际情况和程序员的习惯。 如果二元运算符的第一个操作数不是用户自定义类型,则只能用友元的方式实现,而不能用成员的方式实现。 下列运算符不能用友元函数来重载: = ( )[ ] -> 成员运算符函数的优点: 体现了面向对象的思想。
  • 21. 成员和友元运算符函数class complex{ double re,im; public: complex(double r, double I) { re=r; im=i; } complex( ) { re=im=0; } complex operator+(complex ); complex operator++( ); friend complex operator++(complex); friend complex operator+(complex,complex); //… };双目运算符单目运算符成员函数友元函数
  • 22. 7.6 “++”和“--”的重载
  • 23. 时钟类例子运算符前置++和后置++重载为时钟类的成员函数 操作数是时钟类的对象 前置单目运算符,重载函数没有形参 对于后置单目运算符,重载函数需要有一个整型形参。 实现时间增加1秒钟。
  • 24. #include class Clock //时钟类声明 { public: //外部接口 Clock(int NewH=0, int NewM=0, int NewS=0); void ShowTime(); void operator ++(); //前置单目运算符重载 void operator ++(int); //后置单目运算符重载 private: //私有数据成员 int Hour, Minute, Second; };时钟类例子(续1)
  • 25. void Clock::operator ++() //前置单目运算符重载函数 { Second++; if(Second>=60) { Second=Second-60; Minute++; if(Minute>=60) { Minute=Minute-60; Hour++; Hour=Hour%24; } } cout<<"++Clock: "; }时钟类例子(续2)
  • 26. void Clock::operator ++(int) //后置单目运算符重载 { Second++; if(Second>=60) { Second=Second-60; Minute++; if(Minute>=60) { Minute=Minute-60; Hour++; Hour=Hour%24; } } cout<<"Clock++: "; }时钟类例子(续3)
  • 27. //其它成员函数的实现略 int main( ) { Clock myClock(23,59,59); cout<<"First time output:"; myClock.ShowTime(); myClock++; myClock.ShowTime(); ++myClock; myClock.ShowTime(); return 0; }时钟类例子(续4)
  • 28. 程序运行结果为: First time output:23:59:59 Clock++: 0:0:0 ++Clock: 0:0:1时钟类例子(续5)
  • 29. 7.7 赋值运算符“=”的重载如果没有为类显式地定义赋值运算符,编译器将自动产生一个缺省的赋值运算符函数,它执行以字节为单位的复制操作。 注意operator=()函数的两个重要特点: 1.它有一个引用参数,这防止了对赋值号右边对象进行拷贝。(提高效率) 2.函数返回了引用,而不是对象。 复制构造函数不如使用引用参数和返回引用有效,简便。
  • 30. counter类例子 #include "iostream.h" class Counter{ int number; public: Counter(int iv=0){number=iv;} ~Counter(){} Counter& operator=(const Counter&); Counter& operator+(const Counter&); Counter& operator++(); Counter operator++(int); void print(); };
  • 31. counter类例子(续1)Counter& Counter::operator=(const Counter& c) { number=c.number; return *this; } Counter& Counter::operator++() { ++number; return *this; }
  • 32. counter类例子(续2)Counter Counter::operator++(int) { Counter temp; temp=*this; number++; return temp; } void Counter::print() { cout<
  • 33. counter类例子(续3)int main() { Counter c1(10),c2,c3; c2=++c1; c3=c2++; c1.print(); c2.print(); c3.print(); return 0; }
  • 34. 7.8 运算符”[ ]”与运算符”( )”的重载在C++中,数组下标运算符”[ ]”被认为是双目运算符,并且只能使用类的成员函数进行重载。 重载的operator()必须被声明为成员函数,它的参数表可以有任意数目的参数。
  • 35. 重载数组下标运算符”[ ]”成员函数operator[ ]( 形参 )的通用形式为: 类型 类名::operator[ ]( 形参 ) { 函数体 }
  • 36. 安全数组的例子 A safe array example.#include #include using namespace std; const int SIZE = 3; class atype { int a[SIZE]; public: atype( ) { register int i; for(i=0; i
  • 37. 安全数组的例子 A safe array example.续1int &atype::operator[](int i) { if(i<0 || i> SIZE-1) { cout << "\nIndex value of "; cout << i << " is out-of-bounds.\n"; exit(1); } return a[i]; } int main( ) { atype ob; cout << ob[2]; // 输出 2 cout << " "; ob[2] = 25; // 下标运算符[]出现在赋值运算符的左边 cout << ob[2]; // 输出 25 ob[3] = 44; // 产生运行时错误,下标3超出了数组边界 return 0; }为数组提供边界检查
  • 38. 安全数组的例子 A safe array example.续2程序输出结果如下: 2 25 Index value of 3 is out-of-bounds. 当程序执行下面的语句时 ob[3] = 44; 函数operator[ ]( )将会检测到越界错误并在发生数组越界错误之前结束程序。
  • 39. 7.9 构造函数用于类型转换一、 将类的类型转换为其它数据类型 二、 将其它数据类型转换为类的类型 三、 不同类的类型之间的转换
  • 40. 一、 将类的类型转换为其它数据类型由于C++不提供将用户自定义的类的类型转换为C++内建数据类型的方法,要实现这一操作,我们可利用运算符重载的方法设计一转换函数。 转换函数据的格式为: operator 数据类型();
  • 41. 用户定义类型转换成标准类型的例子#include "iostream.h" const double ratio=8.5; class person{ double RMB; double dollar; public: person(){dollar=RMB=0;} person(double i,double j) { RMB=i;dollar=j;} operator double( );// type conversion function ~person( ){}; };类型转换函数
  • 42. 用户定义类型转换成标准类型的例子(续)person::operator double( ){ return RMB+ratio*dollar; } void main( ) { person A(100,500.5); cout<<"This person's money is: " <<(double)A<<"RMB"<
  • 43. 转换函数设计规则转换函数的设计应遵循以下两条规则: (1)虽然转换函数中有return语句,但是在定义和定义转换函数时,不能指定其返回值类型。 (2)转换函数不能带任何参数,因为转换函数的处理数据为其类对象本身。
  • 44. 二、 将标准数据类型转换为类的类型利用转换函数可以将用户定义的类型转换为C++的标准数据类型 利用构造函数可以将C++的标准数据类型转换为用户定义的类型 参见上例
  • 45. 三、 用户自定义类型之间的转换不同类对象之间的类型转换也是通过转换函数来完成, 要实现类的类型之间的相互转换必须在两个类中都定义转换函数。 参见下面的例子:
  • 46. 用户自定义类型之间的转换实例#include "iostream.h" #include "string.h" #include "stdlib.h" const double ratio=8.5; class RMB; class dollar{ double value; public: dollar( ){value=0.0;} dollar(double i){value=i;} operator RMB( ); operator double( ){return value;} ~dollar( ){}; };类型转换函数
  • 47. 用户自定义类型之间的转换实例(续1)class RMB{ double value; public: RMB(){value=0.0;} RMB(double i){value=i;} operator dollar( ){return dollar(value/ratio);} operator double( ){return value;} ~RMB(){}; }; dollar::operator RMB(){ return RMB(ratio*value); }类型转换函数
  • 48. 用户自定义类型之间的转换实例(续2)void main() { dollar A1(500.5),A2; RMB B1(1600),B2; cout<<"The member A1's money is $ "<
  • 49. 运算符重载小结1. 运算符总是与用户自定义类型联系时被重载。 2. 在重载运算符时,有两个约束条件: 1)运算符的优先权不能改变; 2)运算符所采用的操作数个数不能被改变。 3. 虽然让运算符函数执行任何动作都是可以的——不论该动作是否与传统的运算符运用相联系——但是,最好还是让重载运算符的动作保留在传统用法的核心内为好。 4. 当然,有些时候仍然必须与传统无关的方式使用运算符,例如:<<与>>被重载以支持控制台输入/输出。 5. C++语言中绝大多数的运算符都可以重载,只有少数例外:参见下一页。
  • 50. 不能重载的运算符:: (scope resolution 作用于分辨符) . (member selection 成员选择符) .* (member selection through pointer to function 通过函数指针的成员选择符).