课程设计安排.ppt

上传人:本田雅阁 文档编号:2611656 上传时间:2019-04-18 格式:PPT 页数:131 大小:976.01KB
返回 下载 相关 举报
课程设计安排.ppt_第1页
第1页 / 共131页
课程设计安排.ppt_第2页
第2页 / 共131页
课程设计安排.ppt_第3页
第3页 / 共131页
课程设计安排.ppt_第4页
第4页 / 共131页
课程设计安排.ppt_第5页
第5页 / 共131页
点击查看更多>>
资源描述

《课程设计安排.ppt》由会员分享,可在线阅读,更多相关《课程设计安排.ppt(131页珍藏版)》请在三一文库上搜索。

1、课程设计安排,时间 :2014年1月15-19日 下午13:00-18:00 地点:综合楼315,第四章 类与对象,封装(Encapsulation)是面向对象程序设计最基本的特性,也就是把数据(属性)和函数(操作)合成一个整体,这是用类与对象实现的。接口(类设计)和实现(编程)分离。 本章重点: 1.引入C+的类(class)和对象(object)的概念,建立“函数也可以是数据类型的成员”的思想。 2.运算符重载。,C+: C with Classes,第四章 类与对象,4.1 类与对象,4.5 运算符的重载,4.4 引用与复制构造函数,4.2 从面向过程到面向对象,4.9 名字空间域和类域

2、(选读),4.10面向对象的程序设计和Windows编程,4.8 结构,4.7 静态成员,4.6 友元,4.3 构造函数和析构函数,4.1 类与对象,4.1.3对象的创建与使用,4.1.1 C+类的定义,4.1.2 成员函数的定义,4.1.1 C+类的定义,类的引入: 类是一种数据类型。 描述客观事物必须用不同的数据类型来描述不同的方面。如商品: 商品名称(用字符串描述),该商品数量(用整型数描述),该商品单价(用浮点数描述),该商品总价(用浮点数描述)。 这里用了属于三种不同数据类型的四个数据成员(data member)来描述一种商品。,4.1.1 C+类的定义,类的表述: class C

3、Goods public : char Name21 ; int Amount ; float Price ; float Total_value ; ; /最后的分号不可少,这是一条说明语句 关键字class是数据类型说明符,指出下面说明的是类。标识符CGoods是商品这个类的类型名。花括号中是构成类体的系列成员,关键字public是一种访问限定符。,4.1.1 C+类的定义,访问限定符(access specifier): public(公共的)说明的成员能从外部(类代码外面)进行访问。 private(私有的)和protected(保护的)说明的成员不能从外部进行访问。 每种说明符可在类

4、体中使用多次。 访问限定符的作用域是从该说明符出现开始到下一个说明符之前或类体结束之前结束。 如果在类体起始点无访问说明符,系统默认定义为私有(private用关键字class定义类)。 访问限定符private(私有的)和protected(保护的)体现了类具有封装性(Encapsulation)。,4.1.1 C+类的定义,类的定义: class 类名 private: 成员表1; public: 成员表2; protected: 成员表3; ; /注意:所有说明都以分号结束 其中“class 类名”称为类头(class head)。花括号中的部分称为类体(class body),类体中定

5、义了类成员表(class member list),包含数据和函数。,4.1.1 C+类的定义,成员函数(member function) : class(struct) CGoods private : char Name21 ; int Amount ; float Price ; float Total_value ; public : /声明是必须的,定义可选(放到类内部也可以放到类外部) void RegisterGoods(char,int,float); /输入数据 void CountTotal(void) ; /计算商品总价值 void GetName(char) ; /读取

6、商品名 int GetAmount(void) ; /读取商品数量 float GetPrice(void) ; /读取商品单价 float GetTotal_value(void) ; ; /读取商品总价值,课下查找:利用关键字class和struct定义类的区别?,4.1.1 C+类的定义,封装: 类把数据(事物的属性)和函数(事物的行为操作)封装为一个整体。 接口: 通常数据成员被说明成私有的,函数成员被说明成公有的;从外部对数据成员进行操作,只能通过公有函数来完成,数据受到了良好的保护,不易受副作用的影响。公有函数集定义了类的接口(interface)。 成员函数可以直接使用类定义中的

7、任一成员,可以处理数据成员,也可调用函数成员。,注意: 类是一种数据类型,定义时系统不为类分配存储空间,所以不能对类的数据成员初始化。类中的任何数据成员也不能使用关键字extern、auto或register限定其存储类型。 class CGoods; /类声明,未定义之前,4.1.2 成员函数的定义,函数定义: 通常在类定义中,成员函数仅作声明。函数定义通常在类的定义之后进行,其格式如下: 返回值类型 类名:函数名(参数表) /函数体 其中运算符“:”称为作用域解析运算符(scope resolution operator),它指出该函数是属于哪一个类的成员函数。 类CGoods的函数定义,

8、成员函数,inline声明 class CGoods float GetPrice(void) /类内部定义,默认inline return price; ; 或者 inline float Cgoods: GetPrice()/建议此声明方式 return price; ,成员函数,const 函数声明: 数据成员只可以读取不可以修改 class CGoods float GetPrice(void) const; ; 而且 float Cgoods: GetPrice() const return price; ,定义对象: 对象是类的实例(instance)。定义一种数据类型只是告诉编译

9、系统该数据类型的构造,并没有预定内存。类只是一个样板,以此样板可以在内存中开辟出同样结构的实例对象。格式如下: CGoods Car; 这个定义创建了CGoods类的一个对象Car,同时为它分配了属于它自己的存储块,用来存放数据和对这些数据实施操作的成员函数(代码)。对象只在定义它的域中有效。,4.1.3 对象的创建与使用,对象存储:,图4.1 各对象完全独立地安排内存的方案,图4.1是系统为每一个对象分配了全套的内存。数据区安放成员数据,代码区安放成员函数。 注意:区别同一个类的各个不同的对象的属性是由数据成员决定的,不同对象的数据成员的内容是不一样的;而行为(操作)是用函数来描述的,这些操

10、作的代码对所有对象都是一样的。,图4.2 各对象的代码区共用的方案,图4.2仅为每个对象分配一个数据区,代码区(放成员函数的区域)为各对象类共用。 图4.1对应的是在类说明中定义函数,而图4.2对应的是在类说明外部定义函数 。,4.1.3 对象的创建与使用,内联函数: 使用关键字inline,系统自动采用内联扩展方法实现,每个对象都有该函数一份独立的代码。 如RegisterGoods()函数可定义为: inline void CGoods:RegisterGoods(char name , int amount , float price) strcpy(Name,name) ; Amoun

11、t=amount ; Price=price ; 则每个对象都有RegisterGoods()函数一份独立的代码。,4.1.3 对象的创建与使用,对象使用规则: 只要在对象名后加点号(点操作符,成员访问运算符(member access oprator)之一),再加成员数据或成员函数名就可以了。但是这些成员必须是公有的成员,只有公有成员才能在对象的外面对它进行访问。,【例4.1】商品类对象应用实例,【例4.1】中对象car的4个数据成员全是私有的,如写: car.Name;car. Amount; car.Price;car.Total_value; 是错误的,必须用对象car所带的公有函数进

12、行访问。,4.2 从面向过程到面向对象 (阅读),结构化程序设计特点: 采用的是“自顶向下,逐步细化(divide and conquer,stepwise refinement)”的思想。具体操作方法是模块化。模块是按功能来分的,所以也称功能块。在C+中称为一个函数,一个函数解决一个问题,即实现一个功能或一个操作。,在模块化的思想中已经出现了封装的概念,这个封装是把数据封装到模块中,即局部变量。这是很不彻底的,因为模块是功能的抽象,而数据则是具有其个性的,一但发生变化,抽象的功能模块就不再适用了。可维护性差成了制约结构化程序设计的瓶颈。 面向过程程序设计缺点的根源在于数据与数据处理分离。,4

13、.2 从面向过程到面向对象(阅读),结构化程序设计弱点: 当软件规模过大,采用结构化程序设计,其开发和维护就越来越难控制。其根本的原因就在于面向过程的结构化程序设计的方法与现实世界(包括主观世界和客观世界)往往都不一致,结构化程序设计的思想往往很难贯彻到底。,对象概念: 对象的概念是面向对象技术的核心所在。面向对象技术中的对象就是现实世界中某个具体的物理实体在计算机逻辑中的映射和体现。,4.2 从面向过程到面向对象(阅读),对象,类,计算机世 界,实体,抽象类别,现实世界 客观世界,抽象,抽象,实例化,映射,主观世界,图4.3对象、实体与类,现实世界中的实体可以抽象出类别的概念。对应于计算机世

14、界就有一个类(class)的概念。面向对象是计算机世界模拟现实世界。图4.3表达了计算机世界与现实世界之间的对应关系。,4.2 从面向过程到面向对象(阅读),对象、类与消息: 面向对象程序设计模拟自然界认识和处理事物的方法,将数据和对数据的操作方法放在一起,形成一个相对独立的整体对象(object),同类对象还可抽象出共性,形成类(class )。一个类中的数据通常只能通过本类提供的方法进行处理,这些方法成为该类与外部的接口。对象之间通过消息(message)进行通讯。,4.2 从面向过程到面向对象(阅读),调节旋钮,对 象,4.2 从面向过程到面向对象(阅读),是一个抽象的概念,用来描述某一

15、类对象所共有的、本质的属性和类行为。,类,4.2 从面向过程到面向对象(阅读),我们把对象之间产生相互作用所传递的信息称做消息。,消 息,启 动,转 向,4.2 从面向过程到面向对象(阅读),封装性,对象是一个封装体,在其中封装了该对象的属性和操作。通过限制对属性和操作的访问权限,可以将属性“隐藏”在对象内部,对外提供一定的接口,在对象之外只能通过接口对对象进行操作。,C+通过建立数据类型类来支持封装和数据隐藏。封装性增加了对象的独立性,从而保证了数据的可靠性。一个定义完好的类可以作为独立模块使用。,面向对象程序设计的特点:,4.2 从面向过程到面向对象(阅读),继承与派生,以汽车为例看客观世

16、界描述事物的方式:,当定义了一个类后,又需定义一个新类,这个新类与原来的类相比,只是增加或修改了部分属性和操作,这时可以用原来的类派生出新类,新类中只需描述自己所特有的属性和操作。,面向对象程序设计提供了类似的机制:,继承性大大简化了对问题的描述,大大提高了程序的可重用性,从而提高了程序设计、修改、扩充的效率。,新类称为子类或派生类,原来的类称为基类。派生可以一直进行下去,形成一个派生树。,4.2 从面向过程到面向对象(阅读),语文、数学、英语、政治、物理、化学、生物,多态性,多态性指,同一个消息被不同对象接收时,产生不同结果,即实现同一接口,不同方法。,高中生,计 算平均成绩,大学生,高数、

17、英语、计算机、线性代数,4.2 从面向过程到面向对象(阅读),继承和多态性组合,可以生成很多相似但又独一无二的对象。继承性使得这些对象可以共享许多相似特性,而多态又使同一个操作对不同对象产生不同表现形式。这样不仅提高了程序设计的灵活性,而且减轻了分别设计的负担。,4.3 构造函数和析构函数,特殊的成员函数,只要创建类类型的对象,都要执行构造函数。功能:为数据成员分配存储空间并初始化每个对象的数据成员。 构造函数(constructor),4.3.1 构造函数的定义与使用,4.3.2 析构函数的定义,构造函数特征: 1.函数名与类名相同。 class CGoods public: CGoods

18、(char* name , int amount , float price); CGoods (int amount , float price) const; /error ; 2.构造函数无函数返回类型说明。注意是什么也不写,也不可写void!形式参数可以有也可以没有. 不能声明为const,4.3.1 构造函数的定义与使用,3.在程序运行时,当新的对象被建立,该对象所属的类的构造函数自动被调用,在该对象生存期中也只调用这一次。 CGoods book1; 4.构造函数可以重载。说明中可以有多个构造函数,它们由不同的参数表区分; class CGoods public: CGoods (

19、char* name , int amount , float price); CGoods (char* name , float price); CGoods(); ;,思考:多个构造函数,调用时选择哪一个?重载函数调用规则来思考。,4.3.1 构造函数的定义与使用,5.构造函数可以在类中定义,也可以在类外定义。 class CGoods public: CGoods (char* name , int amount , float price) /类内部定义 strcpy(Name,name) ;Amount=amount ;Price=price ; Total_value=price

20、*amount ; ; 或者 class CGoods public: CGoods (char* name , int amount , float price);/类内部声明 ; CGoods:CGoods (char* name , int amount , float price)/类外部定义 strcpy(Name,name) ;Amount=amount ;Price=price ; Total_value=price*amount ; ,6. 如果类说明中没有给出构造函数,则C+编译器自动创建一个默认的构造函数: 类名(void) 注意:只要我们定义了一个构造函数,系统就不会自动

21、生成默认的构造函数。 只要构造函数是无参的或各参数均有默认值的,C+编译器都认为是默认的构造函数,并且默认的构造函数只能有一个 。,4.3.1 构造函数的定义与使用,CGoods的构造函数: 三参数: CGoods (char* name , int amount , float price) strcpy(Name,name) ;Amount=amount ;Price=price ; Total_value=price*amount ; 两参数:货名和单价, CGoods (char* name , float price) strcpy(Name,name) ;Price=price ;

22、Amount=0 ; Total_value=0.0 ; 默认的构造函数: CGoods() Name0=0 ; Price=0.0 ; Amount=0 ; Total_value=0.0 ; 这三个构造函数同时被说明(重载)。,4.3.1 构造函数的定义与使用,实参决定调用哪个构造函数: CGoods Car1(“夏利2000”,30,98000.0); 调用了CGoods中的第一个构造函数,等效于: CGoods Car1= CGoods(“夏利2000”,30,98000.0);,CGoods Car2(“桑塔那2000”,164000.0) ; 调用的是第二个构造函数,参数为两个。,

23、CGoods Car3; 定义时调用不带参数的构造函数 但是定义对象时不能加括号。例如:CGoods Car4(); Car4()是不带参数的函数,它的返回值是类CGoods的对象。,【例4.1_1】完整商品类对象应用实例,练习,选择一下一个抽象,确定类中需要什么数据,并提供适当的构造函数。并解释你的决定。 1. Book 2. Date 3. Student 4. Vehicle 5. Tree 6. Computer 7. Program,构造函数初始化式,与普通函数一样,构造函数具有名字,形参列表和函数体。不同:定义时可以包含一个初始化列表(不能在声明处指出) CGoods :CGood

24、s (char* name , int amount , float price):Amount(amont),Price(price) strcpy(Name,name); 注意:const,没有默认构造函数的类类型的成员或引用数据成员 class A private: const int i; public: A(int var)var=i; /error A(int var):i(var) ;,成员初始化的次序:,成员被定义的次序决定了初始化的次序.初始化列表只是仅初始化成员的值,而不指定初始化的顺序。 class X int i; int j; public: /run-time er

25、ror: i is initialized before j X(int val):j(val),i(j) ;,4.3.2 析构函数的定义,析构函数(destructor)特征: 当一个对象的生命周期结束时,C+会自动调用析构函数(destructor)对该对象并进行善后工作(不严谨)。 1. 构函数名与类名相同,但在前面加上字符,如 CGoods()。 2. 析构函数无函数返回类型,与构造函数在这方面是一样的。但析构函数不带任何参数。 3. 一个类有一个也只有一个析构函数,这与构造函数不同。析构函数可以默认。 4. 对象注销时,系统自动调用析构函数,按成员在类中声明的次序逆序撤销成员(释放存

26、储空间)。,【例4.2】定义一个矩形类,有些情况,不需要显示定义 析构函数。查找资料,什么 情况下需要什么情况下不需要? 能否举例说明,想一想:什么工作?,4.4 引用与复制构造函数,4.4.1 引用,4.4.2 复制构造函数,4.4.3 成员对象与构造函数,4. 4.1 引用,引用的导入: 参数传递的传值方式在函数域中为参数重新分配内存,而把实参的数值传递到新分配的内存中。它的优点是有效避免函数的副作用。 问题:如果要求改变实参的值,怎么办呢?如果实参是一个复杂的对象,重新分配内存会引起程序执行效率大大下降,怎么办呢? 有一种导出型数据类型引用(reference)可以解决上面的难题。引用又

27、称别名(alias)。,4.4.1 引用,引用的定义: 引用是给一个已经定义的对象/变量重新起一个别名,而不是定义一个新的变量,定义的格式为: 类型 newnum是新定义的引用类型变量,它是变量number的别名。 引用主要用于函数之间的数据传递。,4.4.1 引用,newnum是变量number的别名,C+系统不为引用类型变量分配内存空间。内存分配见下图:,number称为引用newnum的关联变量。“&”(读作ampersand)在这里是引用的说明符。必须注意number和newnum都是double类型。如在程序中修改了newnum也就是修改了number,两位一体。,注意:对数组只能引

28、用数组元素,不能引用数组(数组名本身为地址)。,const 引用(不严谨):指向const 对象的引用 const int val=1024; const int / error: nonconst reference to a const object,课下查找,以下语句是否合法,并解释: int i=34; double j=65.14; const int 可参考课本 p.133,4.4.1 引用,【例4.4】引用作为函数的返回值 一般函数返回值时,要生成一个临时变量作为返回值的副本,而用引用作为返回值时,不生成值的副本。,【例4.5】 返回值为引用的函数作为左值(选读),【例4.3】引

29、用作为函数的参数。 采用引用调用时,将对实参进行操作。,注意:采用引用返回方式时,返回的不能是函数中的局部变量,这时返回的局部变量地址已经失效。引用方式返回最常用的是由引用参数传递过来的变量(见例4.5),其次是全局变量,这样返回的变量地址是有效的。,4.4.2 复制构造函数,复制构造函数引入: 同一个类的对象在内存中有完全相同的结构,如果作为一个整体进行复制是完全可行的。这个复制过程只需要复制数据成员,而函数成员是共用的(只有一份代码)。在建立对象时可用同一类的另一个对象来初始化该对象,这时所用的构造函数称为复制构造函数(Copy Constructor)。 CGoods类,复制构造函数为:

30、 CGoods (CGoods ,复制构造函数形参通常声明为const: CGoods (const CGoods & cgd) 思考: 为什么?,4.4.2 复制构造函数,复制构造函数特征: 1. 复制构造函数的参数必须采用引用。 * 在C+中按值传递一个参数时,会在函数中重新分配一块内存建立与参数同类型的变量或对象,再把参数的数据成员赋给新的变量或对象。在建立这个对象时,编译器就会自动为这个对象调用复制构造函数。如果其参数是真实的对象而不是引用,则又会引入新的一轮调用复制构造函数的过程,出现了无穷递归。,CGoods (CGoods cgd) . . ,4.4.2 复制构造函数,2. 系统

31、会自动提供称为默认的复制构造函数(无显式定义),亦称为默认的按成员初始化。按成员作复制是通过依次复制每个数据成员实现的。 4. 在某些情况下,它对类与对象的安全性和处理的正确性还不够(有指针数据成员或为成员分配资源?),这时就要求提供显式的复制构造函数和复制赋值操作符的定义。 3. 赋值运算符“=”称默认的按成员复制赋值操作符(Overloaded Assignment Operator) ,同类对象之间可以用“=”直接复制 。,4.4.2 复制构造函数,实例: CGood Car1(“夏利2000”,30,98000.00); /调用三个参数的构造函数 CGood Car2= Car1; /

32、调用复制构造函数 Car2=Car1; /调用重载的赋值函数(复制赋值操作) CGood Car3 ( Car1); /调用复制构造函数,Car1为实参 这样三个对象的初始化结果完全一样。,注意: 在类定义中如果没有显式给出构造函数时,并不是不用构造函数,而是由系统自动调用默认的构造函数或默认的复制构造函数。如果有程序设计者定义的构造函数(包括复制构造函数),则按函数重载的规律,调用合适的构造函数。,4.4.2 复制构造函数,隐含的复制构造函数使用: 当函数的形参是非引用(p.125)类型,调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参复制到新的对象中。 2.当

33、函数的返回值是非引用(p.125)类型,函数执行完成返回调用者时使用。理由也是要建立一个临时对象,再返回调用者。 因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。所谓return 对象,实际上是调用复制构造函数把该对象的值拷入临时对象。如果返回的是变量,处理过程类似,只是不调用构造函数。,4.4.3 成员对象与构造函数,聚合(aggregation ) : 类中的成员,除了成员数据和成员函数外,还有成员对象,即用其他类的对象作为类的成员。使用成员对象的技术称为聚合。成

34、员对象是实体,系统不仅为它分配内存,而且要进行初始化。 class studentID ; class student studentID m_id; char name20; student(char *pname, long pid=0):m_id(pid) . ;,4.4.3 成员对象与构造函数,【例4.6】含有成员对象的类的构造函数,含对象成员的析构函数: 因为析构函数没有参数,所以包含成员对象的类的析构函数形式上并无特殊之处。但是撤销该类对象时,会首先调用自己的析构函数,再调用成员对象的析构函数,调用次序与初始化时的次序相反。,4.4.3 成员对象与构造函数,构造函数和析构函数的调用

35、规则: 1. 对全局定义的对象,当程序进入入口函数main之前 对象就已经定义,那时要调用构造函数。整个程序结束时调用析构函数。 2. 对于局部定义的对象,每当程序控制流到达该对象定义处时,调用构造函数。当程序控制走出该局部域 时,则调用析构函数。 3. 对于静态局部定义的对象,在程序控制首次到达该对象定义处时,调用构造函数。当整个程序结束时调 用析构函数。,4.4.3 成员对象与构造函数,在正确定义了构造函数和析构函数的前提下,在一个健康的程序中,每个创建的对象必然有一个而且只有一个撤消动作。,注意:先建立的对象后撤销。,【例4.7】演示对象创建和撤消的对应关系,4.5 运算符的重载,运算符

36、重载的概念: 运算符的重载是特殊的函数重载,必须定义一个函数,并通知C+编译器,当遇到该重载的运算符时调用此函数。这个函数叫做运算符重载函数,通常为类的成员函数。 运算符重载函数定义: 返回值类型 类名:operator重载的运算符(参数表) operator是关键字,它与重载的运算符一起构成函数名。因函数名的特殊性,C+编译器可以将这类函数识别出来。,int a(1),b(2),c; c=a+b; classX x,y,z; z=x+y;,细解运算符重载: 复数类+的重载: Complex Complex:operator+(Complex c) /显式说明局部对象 Complex Temp

37、(Real+c.Real , Image+c.Image) ; /注意:直接写对象c的私有成员,不用调c的公有函数处理 return Temp ; Complex c2,c3; c2+c3; - c2.operator+(c3) ; 函数c2.operator创建一个局部的Complex对象Temp,把出现在表达式中的两个Complex类对象c2和c3的实部之和及虚部之和暂存其内,然后把这个局部对象返回,赋给Complex类对象c(注意这里调用了复制构造函数生成一个无名临时对象过渡)。参见图4.8。,4.5 运算符的重载,class Complex double Real,Image; . ;

38、,思考:此语句被调用到执行完毕,执行了几次构造函数?,Temp.Real=Real+ c2.Real; Temp.Image=Image+ c3.Image; c=return(Temp);,Real Image,c.Real c.Image,=,+,局部对象Temp,当前对象c2,形参对象c,图4.8 显式说明临时对象的“+”运算符执行过程,隐式返回计算结果: 省略局部的Complex对象Temp Complex Complex:operator+(double d) return Complex(Real+d , Image);/隐式说明局部对象 在return后面跟的表达式中调用的是类的

39、构造函数,它为无名对象赋值(初始化),返回值就是该无名对象。,调用两次构造函数,4.5 运算符的重载,说明: Complex Complex:operator+(Complex c) Complex Temp(Real+c.Real , Image+c.Image) ; return Temp ; 当成员函数的参数为同一类(class)的对象或它的引用,在函数体内使用参数对象的私有数据成员时,可用对象名加成员访问操作符点号进行。 从逻辑上讲,每个对象有自己的成员函数,访问同类其他对象的私有数据成员应通过该对象的公有函数,不能直接访问。但在物理上只有一个成员函数代码,所以直接访问是合理的。仅在成

40、员函数中可以这样做。,【例4.8】复数类,应用它进行复数运算,思考: 1. 这样的函数设计有没有问题? 2. 可否改为: Complex ,4.5 运算符的重载,引用作为参数: Complex Complex:operator+(const Complex 注意:参数采用对象的引用而不是对象本身,调用时不再重新分配内存建立一个复制的对象,函数效率会更高。而在引用形式参数类型说明前加const关键字,表示被引用的实参是不可改变的,如程序员不当心在函数体中重新赋值了被引用的实参, C+编译器会认为出错。,调用一次构造函数,4.5 运算符的重载,const引用(指向const对象的引用)进一步说明:

41、 引用在内部存放的是被引用对象的地址,不可寻址的值是不能引用的;当引用作为形参时,实参也不能使用不可寻址的值,更不可能进行类型转换(如:实数转换为整数)。但是const引用不同,它是只读的,为了绝对保证不会发生误改,编译器实现const引用时,生成一个临时对象,引用实际上指向该临时对象,但用户不能访问它。所以const引用可以实现不可寻址的值(包括字面常量)的引用。 例如: double dval=1024; const int 因有临时对象,引用和类型转换都实现了。 当const引用作为形参时,实参也能使用不可寻址的值,并能进行类型转换。,4.5 运算符的重载,默认的复数复制赋值操作符: 函

42、数声明: Complex ,4.5 运算符的重载,本例中重载的赋值运算符“=”取代了默认的赋值操作,返回一个复数临时变量,尽管该复数生命期仅在使用赋值号的表达式(如a=b=c)中,却也能进行连续赋值。但它的执行效率和代码简洁性较差。,课本的例子: Complex Complex:operator=(Complex c)/重载= Complex temp; /定义temp为可返回Complex类型值,使=可连续使用 temp.Real=c.Real; temp.Image=c.Image; Real=temp.Real; Image=temp.Image; return temp; ,4.5 运

43、算符的重载,2. 当用类的成员函数实现运算符的重载时,运算符重载函数的参数(当为双目运算符时)为一个或(当为单目运算符时)没有。运算符的左操作数一定是对象,因为重载的运算符是该对象的成员函数,而右操作数是该函数的参数。(隐含的*this),3. 单目运算符“+”和“-”存在前置与后置问题。 前置“+”格式为: 返回类型 类名:operator+() 而后置“+”格式为: 返回类型 类名:operator+(int) 后置“+”中的参数int仅用作区分。,小结: 1. 运算符重载函数的函数名必须为关键字Operator加一个合法的运算符。在调用该函数时,将右操作数作为函数的实参。,4.5 运算符

44、的重载,4. 优先级和结合性是固定的 x=y+z; x.operator=(y.operator+(z),4.5 运算符的重载,表5.1 C+中不允许重载的运算符,5. C+中只有极少数的运算符不允许重载。,4.5 运算符的重载,问题:double d=05; Complex c; 例5.7中: c=c+d; 语句,改为 c=d+c; 因为d不是Complex的对象,C+编译器将无法找到合适的重载的“+”运算符对应的函数,最终给出出错信息。 怎样解决?,4.6 友元,在C+中友元(friend)函数允许在类外访问该类中的任何成员,就象成员函数一样。友元函数用关键字friend说明。,上节答案:

45、 用友元函数重载运算符“+”,可以实现 c=d+c;,4.6 友元,class Complex friend Complex operator + (double,Complex); ; /opration+说明为类Complex类的友元函数, /friend只用于类说明中,定义时不加friend Complex operator + (double d , Complex c) return Complex(d+c.Real , c.Image) ; /注意友元不是成员函数,但以直接访问私有成员 int main(void) c=d+c1; c.print();return 0; 解释: d

46、+c被C+编译器解释为:operator+(d,c),4.6 友元,友元函数重载运算符形式: +有三种形式。另两个的声明为: friend Complex operator +(Complex , Complex ) ; friend Complex operator + (Complex , double ) ; 涵盖实数与复数,复数与复数,复数与实数相加三种情况。,可以仅使用友元函数 friend complex operator +(complex , complex) ; 实数被默认的构造函数强制转换为虚部为零的复数。d+c1被解释为:operator+(complex(d) , c1

47、) 注意:传值,在函数内是建立了两个复数对象,而把实参的值传进去,进行运算。参见图5.9。,4.6 友元,比较:友元函数可以有两个参数,而对应的成员函数只有一个参数,所以友元函数的使用可以更灵活、更方便。 改进: Operator+友元函数的声明可使用引用类型变量 friend Complex operator+(const Complex & c1, const Complex & c2),图5.9 友元函数operator+执行过程内存分配,【例4.8_1】 用友元函数重载运算符,4.6 友元,单目运算符前“+”的成员函数重载方式如下: Complex Complex:operator+(

48、) return Complex (+Real , +Image) ; 采用成员函数方式重载与使用都很方便。,友元函数重载后置“+”如下: friend Complex operator+(Complex 采用引用类型,后“+”是直接施加于实参。否则施加于副本,而实参不变。,注意:复制赋值运算符(=)重载必须为成员函数,不可为友元函数。因为默认的复制赋值运算符(=)是成员函数,友元函数不能取代它。,友元函数注意点: 1. 友元函数不是类的成员函数,在函数体中访问对象的成员,必须用对象名加运算符“.”加对象成员名。但友元函数可以访问类中的所有成员,一般函数只能访问类中的共有成员。 2. 友元函数不受类中的访问权限关键字限制,可以把它放在类的公有、私有、保护部分,但结果一样。 3. 某类的友元函数的作用域并非该类作用域。如果该友元函数是另一类的成员函数,则其作用域为另一类的作用域,否则与一般函数相同。,友元类:整个类可以是另一个类的友元。友元

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1