第 4 章 类的高级部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.

Slides:



Advertisements
Similar presentations
单元二:面向对象程序设计 任务二:借书卡程序设计.
Advertisements

JAVA 编 程 技 术 主编 贾振华 2010年1月.
Memory Pool ACM Yanqing Peng.
第九讲 类与对象 (I)面向对象基础.
第4章 数组 数组是由一定数目的同类元素顺序排列而成的结构类型数据 一个数组在内存占有一片连续的存储区域 数组名是存储空间的首地址
第八章 类和对象.
C++程序设计 王希 图书馆三楼办公室.
1 Department of Computing.
走向C++之路 WindyWinter WindyWinter感谢诸位前来捧场。
Chap 18 類別與物件 夫有土者,有大物也。有大物者,不可以物。 物而不物,故能物物。 明乎物物者之非物也,豈獨治天下百姓而已哉!
4.1 概述 4.2 类与对象的实现 4.3 对象的初始化和析构 4.4 类的包含 4.5 类模板
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
C++程序设计 第二讲 清华大学软件学院.
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
Scope & Lifetime 前言 Local Scope Global Functions & Objects
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第11章 运算符重载 什么是运算符重载 运算符重载的方法 几个特殊的运算符的重载 自定义类型转换运算符 运算符重载实例.
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
第七章 搜索结构 静态搜索结构 二叉搜索树 AVL树.
Classes: A Deeper Look, Part 1
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
類別樣板 Class Template 類似函式樣板 由類別樣板產生的類別稱為類別樣版的實體(instance)
Operator Overloading; String and Array Objects
第3章 继承和派生.
西安交通大学 计算机教学实验中心 大学C++程序设计教程 西安交通大学 计算机教学实验中心
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
Object-Oriented Programming in C++ 第一章 C++的初步知识
程序设计期末复习 黎金宁
第三章 C++中的C 面向对象程序设计(C++).
第12章 從C到C++語言 12-1 C++語言的基礎 12-2 C++語言的輸出與輸入 12-3 C++語言的動態記憶體配置
2 C++ 的基本語法和使用環境 親自撰寫和執行程式是學好程式語言的不二法門。本章藉由兩個簡單的程式,介紹C++ 程式的基本結構和開發環境,讓初學者能逐漸建立使用C++ 的信心。
第二章 C++对C 在非面向对象方面的改进 更简洁,更安全.
类类型 C++支持的内置类型和操作,如 int i=10; i=i%6; i=i+4;
C++语言程序设计 第十一章 流类库与输入/输出.
切換Dev c++顯示語言 工具->環境選項(V)->介面->language (Chinese TW)
10 多載函數 10.1 多載概論 多載一般函數 多載成員函數 10-3
Classes (2) Lecture 7.
第十三讲 文件流与 输出输入重载.
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
C++大学基础教程 第11章 多态性 北京科技大学 信息基础科学系 2019/4/8 北京科技大学.
Chapter 2 & Chapter 3.
Speaker: Liu Yu-Jiun Date: 2009/4/29
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
第10讲 构造函数和析构函数 构造函数 析构函数 This 指针.
保留字與識別字.
第11章 從C到C++語言 11-1 C++語言的基礎 11-2 C++語言的資料型態與運算子 11-3 C++語言的輸出與輸入
第三章 数据抽象.
字符串 (String) 字符串是 n (  0 ) 个字符的有限序列, 记作 S = “c1c2c3…cn” 其中,S 是串名字
Object-Oriented Programming in C++ 第二章 类和对象
C++大学基础教程 第10章 运算符重载 北京科技大学 2019/5/7 北京科技大学.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
C++程序设计基础 主讲人:谢昕 华东交通大学信息工程学院 第十~十二讲 多态性和虚函数 2005年春季学期.
第九章 物件導向-進階.
第1章 C++面向对象程序设计要点 1.1 函数和函数参数 1.2 输入输出   1.3 类 1.4 抽象类型和模板.
第 9 章 建構函式與解構函式.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第 8 章 标准模板库STL 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
第 3 章 类的基础部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
#include <iostream.h>
方法進階及物件導向基礎 Lecturer: 楊昌樺.
第7章 模板 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
第 5 章 继承、多态和虚函数 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
本章主題 C++的程式結構 資料型態與宣告 算術運算 簡易的輸入輸出指令 程式編譯(Compile)的過程與原理.
《数据结构与算法设计》第一部分 面向对象的C++程序设计基础.
Class 2005/05/25.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
C++语言程序设计(第4版) 第七章 继承与派生 数学与统计科学学院 胡凤珠.
第9章 C++程序设计初步 9.1 C++的特点 9.2 最简单的C++程序 9.3 C++的输入输出 9.4 函数的重载
變數與資料型態  綠園.
Presentation transcript:

第 4 章 类的高级部分 陈哲 副教授 南京航空航天大学 计算机科学与技术学院

主要内容 4.1 静态成员 4.2 友元 4.3 对象赋值问题 4.4 拷贝构造函数 4.5 运算符重载 4.6 对象组合

4.1 静态成员 例:一个学生类,定义其对象张三、李四,他们分别维护着类成员的一份副本(学号、姓名、籍贯等)。 如果要统计一个班学生总数? 4.1 静态成员 例:一个学生类,定义其对象张三、李四,他们分别维护着类成员的一份副本(学号、姓名、籍贯等)。 如果要统计一个班学生总数? 用类外的变量记录,违背了数据封装。 用类的一个数据成员记录,导致多个副本,不仅冗余,而且势必造成数据不一致。 全局对象不好,但复杂程序都是由许多程序员共同设计的,因此需要这种性质的对象。 使用类中的静态数据成员——解决访问权限控制问题。 使用静态成员函数——解决操作合法性控制问题。

4.1.1 静态数据成员 1. 用关键字static声明; 2. 同一个类中的所有对象都共享该变量; 4.1.1 静态数据成员 1. 用关键字static声明; 2. 同一个类中的所有对象都共享该变量; 3. 必须在类外定义和初始化,用(::)来指明所属的类。 4. 静态变量不依赖于对象而存在,无论是否定义该类的对象,这种类型的变量都存在。静态数据成员实际上是在类外定义的一个变量,它的生存期和整个程序的生存期一样,在定义对象之前,静态数据成员就已经存在。

class StaticDemo { static int x ; int y ; public: void putx( int a){ x=a ; } void puty( int b ){ y=b ; } int getx( ) { return x ; } int gety( ) { return y ; } } ; int StaticDemo::x ;

// 静态变量x将被StaticDemo类的所有对象共享,例如: StaticDemo obj1, obj2 ; obj1.putx(5) ; obj1.puty( l0 ) ; obj2.puty(20 ) ; cout << "x: "<< obj1.getx( ) << " " << obj2.getx( ) << endl ; cout << "y: "<< obj1.gety( ) <<" "<< obj2.gety( ) << endl ;

4.1.2 静态函数成员 静态函数成员是类中的一个函数,有static修饰。 4.1.2 静态函数成员 静态函数成员是类中的一个函数,有static修饰。 静态函数成员和静态数据成员类似,在对象生成之前也已经存在。这就是说在对象产生之前,静态的函数成员就能访问其它静态成员。 类外代码可以使用类名和作用域操作符来调用静态成员函数。 静态成员函数只能引用属于该类的静态数据成员或静态成员函数。见例【例4-2】。 注意: #include <iostream.h> class StatDemo { static int x; int y; public: StatDemo(int y=0){ this->y=y; x++; } int getx( ) { return x; } int gety( ) { return y; } static void print( ){ // new function cout<< x<<endl; }; int StatDemo::x=0; // definition & initialization void main( ) { StatDemo o1(10),o2; cout<<o1.getx( )<<endl; StatDemo::print( ); o1.print( );

办公室预算 公 司 总 预 算 子公司1预算 子公司2预算 子公司3预算 子公司4预算

// budget2.h文件的内容 。 class Budget { static float corpBudget; float divBudget; public: Budget( ) { divBudget = 0; } void addBudget( float b) { divBudget += b; corpBudget += divBudget; } static void mainOffice( float ); float getDivBudget( ) { return divBudget; } float getCorpBudget( ){ return corpBudget;} };

// Contents of budget2.cpp #include "budget2.h" float Budget::corpBudget = 0 ; // Definition of static member function. void Budget::mainOffice(float moffice) { corpBudget += moffice; }

//主程序pr4-2.cpp的内容 #include " budget2.h“ void main( ) { float amount; int i; float bud; cout << "Enter main office's budget request: "; cin >> amount; Budget::mainOffice(amount); Budget divisions[4];

for ( i = 0; i < 4; i++) { cout << "Enter the budget for Division "; cout << (i + 1) << " " ; cin >> bud; divisions[i].addBudget(bud); } cout << "\n Here are the division budget :\n"; { cout << "\t Division" << (i + 1) << "\t $ "; cout << divisions[i].getDivBudget( ) << endl;

budget2.h budget2.cpp 4-2.cpp cout << "\t Total Requests: "; cout << divisions[0].getCorpBudget( ) << endl; } budget2.h budget2.cpp 4-2.cpp 1. 对于静态的函数成员,是通过类名和作用域分辨符调用的。 2. 也可以采用对象点的方式调用

4.2 友元函数 引入友元的原因? 1. 友元函数不是类中的函数成员,但它和类的函数成员一样,可以访问类中定义的私有成员。 4.2 友元函数 引入友元的原因? 1. 友元函数不是类中的函数成员,但它和类的函数成员一样,可以访问类中定义的私有成员。 2. 友元函数可以是一个外部函数,也可以是另外一个类的函数成员。 3. 将某个函数声明为一个类的友元方式,前面加friend 。

4.2.1 外部函数作为类的友元 【补例】求两个点之间的距离。 class Point { int xPos, yPos ; public: 4.2.1 外部函数作为类的友元 【补例】求两个点之间的距离。 class Point { int xPos, yPos ; public: Point(int xx=0, int yy=0 ) { xPos=xx; yPos=yy; } int GetXPos( ) { return xPos; } int GetYPos( ) { return yPos; } friend double Distance(Point &a, Point &b); };

double Distance( Point & a, Point & b) { double dx=a.xPos-b.xPos; double dy=a.yPos-b.yPos; return sqrt(dx*dx+dy*dy); } void main( ) Point p1(3, 5), p2(4, 6); cout<< Distance(p1, p2) <<endl; 不采用友元如何解决?

4.2.2 类的成员函数作为另外一个类的友元 其他类的成员函数声明为一个类的友元函数,这个成员函数也称为友元成员。 4.2.2 类的成员函数作为另外一个类的友元 其他类的成员函数声明为一个类的友元函数,这个成员函数也称为友元成员。 友元成员不仅可以访问自己所在类对象中的私有成员和公有成员,还可以访问friend声明语句所在类对象中的私有成员和公有成员,这样能使两个类相互合作完成某一任务。 例:将Aux类的函数addBudget声明为Budget类的友元函数。 Auxiliary

办公室预算 子公司1预算 辅助办公室预算 公 司 总 预 算 子公司2预算 辅助办公室预算 子公司3预算 辅助办公室预算 子公司4预算 辅助办公室预算

class Budget ; // 对Budget类超前使用说明 class Aux // Aux类的定义 { private: float auxBudget ; public: Aux( ) { auxBudget = 0 ; } void addBudget( float , Budget & ) ; float getDivBudget( ) { return auxBudget ; } } ;

class Budget // Budget class declaration { static float corpBudget; float divBudget; public: Budget( ) { divBudget = 0; } void addBudget(float B) { divBudget += B; corpBudget += divBudget; } float getDivBudget( ) { return divBudget; } float getCorpBudget( ) { return corpBudget; } static void mainOffice( float ); friend void Aux::addBudget(float, Budget &); };

// Contents of budget3.cpp #include "budget3.h" // Definition of static member of Budget class float Budget::corpBudget = 0; // Definition of static member function mainOffice. void Budget::mainOffice(float moffice ) { corpBudget += moffice; }

// Definition of member function mainOffice. // Contents of auxi1.cpp #include "auxi1.h" #include "budget3.h" // Definition of member function mainOffice. void Aux::addBudget(float b, Budget &div) { auxBudget += b; div.corpBudget += auxBudget; } 注意!

// Contents of main program #include "budget3.h“ void main( ) { float amount; int i; float bud; cout << "Enter the main office's budget: "; cin >> amount; Budget::mainOffice(amount); Budget divisions[4]; Aux auxOffices[4];

for (i = 0; i < 4; i++) { cout << "Enter the budget request for division "; cout << (i + 1) << ": "; cin >> bud; divisions[i].addBudget(bud); cout << "Enter the budget request for division"; cout << (i + 1) << "'s auxiliary office: "; auxOffices[i].addBudget(bud, divisions[i]); }

cout << "Here are the division budget requests:\n"; for (i = 0; i < 4; i++) { cout << "\tDivision " << (i + 1) << "\t\t\t "; cout << setw(7); cout << divisions[i].getDivBudget( ) << endl; cout << "\tAuxilary Office of Division " <<(i+1); cout << "\t"; cout << auxOffices[i].getDivBudget( ) << endl; } cout << "\tTotal Requests (including main office):"; cout << divisions[0].getCorpBudget( ) << endl; auxi1.h budget3.h auxi1.cpp budget3.cpp 4-3.cpp

4.2.3 一个类作为另外一个类的友元 若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员。 class A 4.2.3 一个类作为另外一个类的友元 若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员。 class A { int x; friend class B; public: void Display( ){ cout<<x<<endl;} } ; class B { A a; void Set(int i) { a.x = i; } void Display( ) { a.Display( ); } };

4.3 对象赋值问题 采用赋值运算符“=”可以将一个对象赋值给另外一个对象,或者采用一个对象初始化另外一个对象。在缺省情况下,这两个操作执行的是对象成员之间的拷贝,也称为按位拷贝或浅拷贝。 【例4-4】对象赋值(浅拷贝)。

class Rectangle { float width , length , area; void calcArea( ) { area = width * length; } public: void setData(float w, float l) { width = w; length = l; calcArea( ); } float getWidth( ) { return width; } float getLength( ) { return length; } float getArea( ) { return area; } };

cout << "\n Box l's Area: "<< box1.getArea( ); void main( ) { Rectangle box1, box2; box1.setData(10, 20); box2.setData(5, 10); cout << "\n Box l's Area: "<< box1.getArea( ); cout << "\n Box 2's Area: "<< box2.getArea( ); box2 = box1; // 对象赋值 } 4-4.cpp

4.3 对象赋值问题(continued) 当采用一个对象初始化另一个对象时,对象成员之间的赋值也是按位拷贝。 赋值和初始化的区别:赋值出现在两个对象都已经存在的情况下,而初始化出现在创建对象时,例如: Rectangle box1; box1. setData(100, 50); Rectangle box2 = box1; // initialization

4.4 拷贝构造函数 拷贝构造函数是一个特殊的构造函数,当定义一个对象并采用同类型的另外一个对象初始化时,将自动调用拷贝构造函数。 4.4 拷贝构造函数 拷贝构造函数是一个特殊的构造函数,当定义一个对象并采用同类型的另外一个对象初始化时,将自动调用拷贝构造函数。 通常,采用缺省的按位拷贝操作也能正确地实现赋值,但在某些情况下不能正确执行。 Example:

PersonInfo(char *n, int a) { name = new char[strlen(n) + 1]; class PersonInfo { char *name; int age; public: PersonInfo(char *n, int a) { name = new char[strlen(n) + 1]; strcpy(name, n); age = a; }  ~PersonInfo ( ) { delete [ ] name; } char *getName ( ) { return name; } int getAge ( ) { return age; } }; PersonInfo person1("Jones", 20); PersonInfo person2 = person1; // 将产生??? 可以运行该程序给学生看结果,分析原因。引入拷贝构造函数的解决方法

PersonInfo (PersonInfo &ob j) 1. 解决的方法:定义拷贝构造函数。 2. 拷贝构造函数是一个特殊的构造函数,当采用一个对象初始化另一个对象时,将自动调用该函数。 PersonInfo (PersonInfo &ob j) { name = new char [strlen(obj .name) + 1]; strcpy(name, obj.name); age = obj.age; } 拷贝构造函数的参数代表“=”运算符右边的对象: PersonInfo person2 = person1 ;

进一步演化:Using const Parameters PersonInfo ( const PersonInfo &ob j) { name = new char [strlen(obj .name) + 1]; strcpy(name, obj.name); age = obj.age; }

4.4.1 缺省的拷贝构造函数 如果一个类没有定义拷贝构造函数,C++将为其创建一个缺省的拷贝构造函数。 4.4.1 缺省的拷贝构造函数 如果一个类没有定义拷贝构造函数,C++将为其创建一个缺省的拷贝构造函数。 缺省的拷贝构造函数的功能就是按位赋值。

4.4.2 调用拷贝构造函数的情况 用对象初始化同类的另一个对象(P129)。 例如: 4.4.2 调用拷贝构造函数的情况 用对象初始化同类的另一个对象(P129)。 例如: PersonInfo st1("ZhangSan" , 20 ); PersonInfo st2( st1) , st3 = st1 ;

2. 如果函数的形参是对象,当进行参数传递时将调用拷贝构造函数(P129) 。 void changePerson ( PersonInfo p ) { //… }   void main ( ) { PersonInfo st1("Susan" , 20 ) ; changePerson ( st1 ) ; 思考:为什么拷贝构造函数的参数一定是个引用,而不是对象?

3. 如果函数的返回值是对象,函数执行结束时,将调用拷贝构造函数对无名临时对象初始化(P129) 。 class PersonInfo { public: PersonInfo ( ) { cout<<"调用构造函数\n" ; } PersonInfo (PersonInfo &obj ){ cout<<"调用拷贝\n" ; } ~PersonInfo ( ) { cout<<"调用析构函数\n" ; } } ;  PersonInfo getPerson ( ) { PersonInfo person ; return person ; // 函数的返回值是对象 }  void main ( ) { PersonInfo student ; student = getPerson ( ) ; 4-5.cpp

注意:VC 2005 版本有问题,VC6正确。

如果函数返回值是对象,要考虑return语句的效率。 例如: return string(s1 + s2); 表示“创建一个无名的临时对象并且将它返回” 与“先创建一个局部对象temp并返回”不等价: string temp(s1 + s2); return temp;

4.4.3 拷贝构造函数中的常参数 // 拷贝构造函数的参数为常引用 PersonInfo( const PersonInfo &obj ) 4.4.3 拷贝构造函数中的常参数 // 拷贝构造函数的参数为常引用 PersonInfo( const PersonInfo &obj ) { name = new char [ strlen( obj.name ) + 1 ] ; strcpy( name, obj.name ) ; age = obj.age ; } 功能:防止程序员无意间修改参数对象。

补充:编译器的一个纰漏 class InvoiceItem { public: InvoiceItem( int size = 51) { cout << "调用缺省构造函数!\n"; } InvoiceItem( char *d ) { cout << "调用一个参数的构造函数!\n"; } InvoiceItem( char *d , int u) { cout << "调用两个参数的构造函数!\n"; } InvoiceItem( InvoiceItem & obj ) { cout << "调用拷贝构造函数!\n"; } ~InvoiceItem( ) { cout << "调用析构函数!\n"; } } ;

补充:编译器的一个纰漏 注意:VC 2005 和 VC6 都不正确。 int main( ) { InvoiceItem Inventory[5]={ // 对象数组 InvoiceItem("鼠标", 100), // A 行 InvoiceItem("硬盘"), // B 行 "主板", // C 行 99 // D 行 } ; return 0; } 4-6.cpp 注意:VC 2005 和 VC6 都不正确。

4.5 运算符重载 Example 1: The / operator can perform two types of division: floating point and integer. Example 2: Date today( 2016, 6, 7); today.add (5); today += 5; // ???

4.5.1 重载赋值运算符 如果对象中有指针成员,采用拷贝构造函数能解决对象初始化问题,但并不能处理对象赋值。 Example: 4.5.1 重载赋值运算符 如果对象中有指针成员,采用拷贝构造函数能解决对象初始化问题,但并不能处理对象赋值。 Example: 假设 PersonInfo 具有拷贝构造函数: PersonInfo person1("ZhangSan” , 20 ) , person2("John",24) ; person2 = person1; // memberwise assignment

Overload the = operator : class PersonInfo { char *name; int age; public: … … void operator = (const PersonInfo &right) { delete [ ] name; name = new char[strlen(right.name) + 1]; strcpy(name, right.name); age = right.age; } };

operator= 函数的参数不一定是常引用,上述声明优点 : (1) 效率高。采用引用可以防止参数传递时生成对象拷贝,节省了对象初始化和析构的过程。 (2) 将参数声明为const,可以防止函数无意间修改对象right的内容。 (3) 符合赋值运算的常识。 函数调用: p2.operator = (p1);  p2 = p1; p3 = p2 = p1; // ???

4.5.2 this指针 this是一个隐含的内嵌指针,它指向调用成员函数的当前对象。 Example: cout << person1.getName( ) << endl; cout << person2.getName( ) << endl;

【例4-7】 class PersonInfo { char *name; int age; public: PersonInfo operator=(const PersonInfo &right) { delete [ ] name; name = new char[strlen(right.name)+ 1]; strcpy(name, right.name); age = right.age; return *this; } // 其他函数略 };

PersonInfo jim("Jim", 20), bob("Bob", 21), clone = jim; void main( ) { PersonInfo jim("Jim", 20), bob("Bob", 21), clone = jim; clone = bob = jim; // Call overloaded = operator cout<< clone.getName( )<< ", " << clone.getAge( )<<endl; } operator=还有问题吗?

PersonInfo &operator=( const PersonInfo &right ) { // 重载运算符“=”的秘笈: // 一、检查自赋值 if( this == &right ) return *this; // 二、释放原有的内存空间 delete [ ] name ; // 三、分配新的内存空间,并复制内容 name = new char[ strlen(right.name )+ 1] ; strcpy( name, right.name ) ; age = right.age ; // 四、返回本对象的引用 return *this ; }

this指针是以隐含参数的形式传递给非静态的函数成员: PersonInfo(PersonInfo *this, char *name, int age ) { this->name = new char[ strlen( name ) + 1] ; strcpy( this->name, name ) ; this->age = age ; }

4.5.4 重载双目算术运算符 引入原因: Feetinches length1(3,5), length2(6,3), length3; 4.5.4 重载双目算术运算符 引入原因: Feetinches length1(3,5), length2(6,3), length3; length3 = length1 + length2; // 等价于 length3 = length1.operator + ( length2 ); 仅讲述如何重载双目运算符+和-,其它类似。

class FeetInches { int feet, inches; void simplify( ); public: // 其他函数代码见4.5.3节 … … FeetInches operator + (const FeetInches &); FeetInches operator - (const FeetInches &); };

// Overloaded binary + operator. FeetInches FeetInches::operator + ( const FeetInches &right ) { FeetInches temp; temp.inches = inches + right.inches; temp.feet = feet + right.feet; temp.simplify( ); return temp; }

// Overloaded binary - operator. FeetInches FeetInches::operator - ( const FeetInches &right) { FeetInches temp; temp.inches = inches - right.inches; temp.feet = feet - right.feet; temp.simplify( ); return temp; }

4.5.4 重载双目算术运算符 任何一个双目算术运算符B被重载以后,当执行二元运算时: Obj1 B Obj2 4.5.4 重载双目算术运算符 任何一个双目算术运算符B被重载以后,当执行二元运算时: Obj1 B Obj2 完全等价于:Obj1.operator B( Obj2 )

4.5.5 重载单目算术运算符 Example 1: distance2 = ++distancel; // 前置++ 等价于: 4.5.5 重载单目算术运算符 Example 1: distance2 = ++distancel; // 前置++ 等价于: distance2 = distancel.operator ++ ( ); Example 2: distance2 = distancel++; // 后置++ distance2 = distancel.operator ++ (0);

FeetInches operator + (const FeetInches &); class FeetInches { int feet, inches; void simplify( ); public: … … FeetInches operator + (const FeetInches &); FeetInches operator - (const FeetInches &); FeetInches operator++( ); FeetInches operator++( int ); }; Dummy parameter

// Overloaded prefix ++ operator. FeetInches FeetInches::operator++( ) { ++inches; simplify ( ); return *this; }

// Overloaded postfix ++ operator. FeetInches FeetInches::operator++(int) { FeetInches temp(feet, inches); inches++; simplify( ); return temp; }

if ( distancel < distance2) 4.5.6 重载关系运算符 重载关系运算符,实现两个对象的比较,其中关系运算符函数要返回一个布尔值(true或false) : Example: if ( distancel < distance2) { ... code ... }

bool operator> (const FeetInches & ); class FeetInches { int feet , inches; void simplify( ); public: … … bool operator> (const FeetInches & ); bool operator< (const FeetInches & ); bool operator==(const FeetInches & ); };

// Overloaded > operator. bool FeetInches::operator > ( const FeetInches &right ) { if (feet > right.feet) return true; else if (feet==right.feet && inches> right.inches) else return false; }

// Overloaded < operator. bool FeetInches::operator < ( const FeetInches &right) { if (feet < right.feet) return true; else if (feet == right.feet && inches < right.inches) return true; return false; }

// Overloaded == operator. bool FeetInches::operator==( const FeetInches &right) { if (feet == right.feet && inches == right.inches) return true; else return false; }

4.5.6 重载流操作符<<和>> 4.5.6 重载流操作符<<和>> Example: int value ; cin >> value; cout << value ; FeetInches distance ; cin >> distance; cout << distance; 注意:如果要为FeetInches类重载流插入符<<,那么必须通过友元函数的形式实现函数重载。 能否实现?

class FeetInches { int feet , inches; public: … … friend ostream &operator<<( ostream &, FeetInches &); friend istream &operator>>( istream &, };

// Overloaded << operator. ostream &operator<<( ostream &strm, FeetInches &obj) { strm << obj.feet << "feet, " << obj.inches <<" inches"; return strm; }

// Overloaded >> operator. istream &operator>>( istream &strm, FeetInches &obj) { cout << "Feet: " ; strm >> obj.feet; cout << "Inches: "; strm >> obj.inches; return strm; }

cout << distance1 <<" "<< distance2 << endl ; 等价于如下过程: 1. 首先调用重载函数<<,执行cout << distancel,返回cout对象; 2. 执行cout << “ ”,返回值是cout对象; 3. 以(1)的方式,执行cout << distance2; 4. 以(2)的方式,执行表达式中的cout << endl; 4-g.cpp

4.5.8 重载类型转换运算符 Example: int i=100; float f=3.14f; i=f; f=i; 4.5.8 重载类型转换运算符 Example: int i=100; float f=3.14f; i=f; f=i; 对于一个对象,通过重载类型转换函数,可实现类型转换功能。

// Truncates the inches value operator int( ) { return feet; } }; No return type is specified in the function header. Since the function is a FeetInches-to-float conversion function, it will always return a float. class FeetInches { int feet , inches; public: … … operator float( ); // Truncates the inches value operator int( ) { return feet; } };

// Convert a FeetInches object to a float. FeetInches::operator float( ) { float temp = feet; temp += (inches / 12.0f); return temp; }

{ FeetInches distance; float f; int i; cin >> distance; void main( ) { FeetInches distance; float f; int i; cin >> distance; f = distance; i = distance; } 4-8.cpp

4.5.8 重载[ ]操作符 Example 1: string name = "John"; cout << name[0]; 4.5.8 重载[ ]操作符 Example 1: string name = "John"; cout << name[0]; name[0]='a'; C++除了支持重载传统的运算符,还支持重载[ ] 符。这样就能象操作普通数组一样操作对象数组。

class IntArray { int *aptr; int arraySize; void memError( ); void subError( ); public: IntArray( int ); IntArray( const IntArray & ); ~IntArray( ); int size( ) { return arraySize; } int &operator[ ]( const int & ); }; why?

// Constructor for IntArray class IntArray::IntArray( int s ) { arraySize = s; aptr = new int [s]; if (aptr == 0) memError( ); for (int i = 0; i < arraySize; i++) *(aptr + i) = 0; }

// Copy Constructor for IntArray class. IntArray::IntArray(const IntArray &obj) { arraySize = obj.arraySize; aptr = new int [arraySize]; if (aptr == 0) memError ( ); for(int i = 0; i < arraySize; i++) *(aptr + i) = *(obj.aptr + i); }

// Destructor for IntArray class. IntArray::~IntArray( ) { if (arraySize > 0) delete [ ] aptr; arraySize=0; }

// memError function. void IntArray::memError( ) { cout << "Cannot allocate memory. \n"; exit(0); } // subError function. void IntArray::subError( ) cout << "Subscript out of range. \n";

// Overloaded [ ] operator. int & IntArray::operator[ ] (const int &sub) { if ( sub<0 || sub >= arraySize) subError( ); return aptr[sub]; }

cout << table[x] << " " ; cout << endl; void main( ) { IntArray table(10); int x; for ( x = 0; x < 10; x++) table[x] = table[x] + 5; cout << table[x] << " " ; cout << endl; cout << "store a value in table[11].\n"; table[11] = 0; } 思考:由于table[x] 重载时为一个引用,如果[]中是一个2+1似的表达式,那么为什么也可以? 4-9.cpp

4.5.9 重载运算符时要注意的问题 通过运算符重载,虽然可以改变运算符的原义,但最好不要改变。 4.5.9 重载运算符时要注意的问题 通过运算符重载,虽然可以改变运算符的原义,但最好不要改变。 运算符重载不能改变运算符原来要求的参数个数。 尽管可以重载C++中的大部分运算符,但有些运算符是不允许重载的。

The operators that cannot be overloaded: ?: . .* :: sizeof operators that may be overloaded: + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- ->* , -> [ ] ( ) new delete

4.5.10 运算符重载综合举例 —自定义string类 自动实现内存的动态分配,程序员无须关心为一个数组要分配多少个字节的空间; 可将string对象直接赋给MyString对象; 采用+=操作符可以连接两个MyString对象; 采用= =、<、>和!=操作符可以实现两个对象的比较,无须调用strcmp函数; 可模仿该类,增加新的重载函数,实现新功能。 自学该节。

4.6 对象组合 结构体支持嵌套,即在定义一个结构体类型时,可以嵌套结构体类型的变量。 4.6 对象组合 结构体支持嵌套,即在定义一个结构体类型时,可以嵌套结构体类型的变量。 类也可以出现这种情况,即让某个类的对象作为另一个类中的数据成员出现,这就是对象组合。 构造函数调用顺序:先调用内嵌对象的构造函数(按内嵌时的声明顺序,先声明者先构造)。然后调用本类的构造函数。(析构的顺序相反) 若调用缺省构造函数(即无形参的),则内嵌对象的初始化也将调用相应的缺省构造函数。 4-h.cpp

Account savings ; //储蓄账户 Account check ; //支票账户 class Customer { public: MyString name ; MyString address ; MyString city ; MyString state ; MyString zip ; Account savings ; //储蓄账户 Account check ; //支票账户 Customer( char *n , char *a , char *c , char *s , char *z ) : name(n) , address(a), city(c), state(s), zip(z) { } } ;

int main( ) { Customer ZhangSan( "Zhang San", "YuDao Street 29", "Nanjing", "Jiangsu", "210016") ; ZhangSan.savings.makeDeposit(1000 ) ; ZhangSan.check.makeDeposit(500 ) ; … }