C 程序设计课件:第四章 类与对象_第1页
C 程序设计课件:第四章 类与对象_第2页
C 程序设计课件:第四章 类与对象_第3页
C 程序设计课件:第四章 类与对象_第4页
C 程序设计课件:第四章 类与对象_第5页
已阅读5页,还剩128页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第四章 类与对象封装(Encapsulation)是面向对象程序设计最基本的特性,也就是把数据(属性)和函数(操作)合成一个整体,这是用类与对象实现的。 本章重点:1.引入C+的类(class)和对象(object)的概念,建立“函数也可以是数据类型的成员”的思想。2.运算符重载。第四章 类与对象4.1 类与对象 4.5 运算符的重载 4.4 引用与复制构造函数 4.2 从面向过程到面向对象 4.9 名字空间域和类域(选读) 4.10面向对象的程序设计和Windows编程 4.8 结构4.7 静态成员 4.6 友元 4.3 构造函数和析构函数附录:用UML类图表达类间关系4.1 类与对象 4.

2、1.3对象的创建与使用 4.1.1 C+类的定义 4.1.2成员函数的定义 4.1.1 C+类的定义 类的引入: 类是一种数据类型。 描述客观事物必须用不同的数据类型来描述不同的方面。如商品: 商品名称(用字符串描述),该商品数量(用整型数描述),该商品单价(用浮点数描述),该商品总价(用浮点数描述)。这里用了属于三种不同数据类型的四个数据成员(data member)来描述一种商品。4.1.1 C+类的定义类的表述:class CGoods public : char Name21 ; int Amount ; float Price ; float Total_value ; ; /最后的

3、分号不可少,这是一条说明语句关键字class是数据类型说明符,指出下面说明的是类。标识符CGoods是商品这个类的类型名。花括号中是构成类体的系列成员,关键字public是一种访问限定符。4.1.1 C+类的定义访问限定符(access specifier):public(公共的)说明的成员能从外部进行访问。private(私有的)和protected(保护的)说明的成员不能从外部进行访问。 每种说明符可在类体中使用多次。 访问限定符的作用域是从该说明符出现开始到下一个说明符之前或类体结束之前结束。 如果在类体起始点无访问说明符,系统默认定义为私有(private)。 访问限定符private

4、(私有的)和protected(保护的)体现了类具有封装性(Encapsulation)。4.1.1 C+类的定义类的定义:class 类名private: 成员表1; public: 成员表2; protected: 成员表3; /注意:所有说明都以分号结束其中“class 类名”称为类头(class head)。花括号中的部分称为类体(class body),类体中定义了类成员表(class member list),包含数据和函数。4.1.1 C+类的定义成员函数(member function) :class CGoodsprivate : char Name21 ; int Amou

5、nt ; float Price ; float Total_value ;public : void RegisterGoods(char,int,float); /输入数据 void CountTotal(void) ; /计算商品总价值 void GetName(char) ; /读取商品名 int GetAmount(void) ; /读取商品数量 float GetPrice(void) ; /读取商品单价 float GetTotal_value(void) ; ; /读取商品总价值 4.1.1 C+类的定义封装:类把数据(事物的属性)和函数(事物的行为操作)封装为一个整体。 接口

6、: 通常数据成员被说明成私有的,函数成员被说明成公有的;从外部对数据成员进行操作,只能通过公有函数来完成,数据受到了良好的保护,不易受副作用的影响。公有函数集定义了类的接口(interface)。 成员函数可以直接使用类定义中的任一成员,可以处理数据成员,也可调用函数成员。注意: 类是一种数据类型,定义时系统不为类分配存储空间,所以不能对类的数据成员初始化。类中的任何数据成员也不能使用关键字extern、auto或register限定其存储类型。 4.1.2成员函数的定义 函数定义:通常在类定义中,成员函数仅作声明。函数定义通常在类的说明之后进行,其格式如下:返回值类型 类名:函数名(参数表)

7、 /函数体其中运算符“:”称为作用域解析运算符(scope resolution operator),它指出该函数是属于哪一个类的成员函数。类CGoods的函数定义定义对象: 对象是类的实例(instance)。定义一种数据类型只是告诉编译系统该数据类型的构造,并没有预定内存。类只是一个样板,以此样板可以在内存中开辟出同样结构的实例对象。格式如下:CGoods Car;这个定义创建了CGoods类的一个对象Car,同时为它分配了属于它自己的存储块,用来存放数据和对这些数据实施操作的成员函数(代码)。对象只在定义它的域中有效。4.1.3对象的创建与使用对象存储: 图4.1 各对象完全独立地安排内

8、存的方案 图4.1是系统为每一个对象分配了全套的内存。数据区安放成员数据,代码区安放成员函数。 注意:区别同一个类的各个不同的对象的属性是由数据成员决定的,不同对象的数据成员的内容是不一样的;而行为(操作)是用函数来描述的,这些操作的代码对所有对象都是一样的。数据区代码区对象1对象数据区代码区对象数据区代码区图4.2 各对象的代码区共用的方案数据区对象1数据区对象数据区对象公共代码区 图4.2仅为每个对象分配一个数据区,代码区(放成员函数的区域)为各对象类共用。 图4.1对应的是在类说明中定义函数,而图4.2对应的是在类说明外部定义函数 。4.1.3对象的创建与使用内联函数:使用关键字inli

9、ne,系统自动采用内联扩展方法实现,每个对象都有该函数一份独立的代码。如RegisterGoods()函数可定义为:inline void CGoods:RegisterGoods(char name , int amount , float price)strcpy(Name,name) ; Amount=amount ; Price=price ; 则每个对象都有RegisterGoods()函数一份独立的代码。注意:inline只是一个建议,最后由编译器决定是否执行。对象的存储方式是物理的,这是由计算机来完成的,它并不影响类在逻辑上的封装性。4.1.3对象的创建与使用对象使用规则: 只要

10、在对象名后加点号(点操作符,成员访问运算符(member access oprator)之一),再加成员数据或成员函数名就可以了。但是这些成员必须是公有的成员,只有公有成员才能在对象的外面对它进行访问。【例4.1】商品类对象应用实例【例4.1】中对象car的4个数据成员全是私有的,如写:car.Name;car. Amount;car.Price;car.Total_value;是错误的,必须用对象car所带的公有函数进行访问。4.2从面向过程到面向对象 (阅读)结构化程序设计特点:采用的是“自顶向下,逐步细化(divide and conquer,stepwise refinement)”的

11、思想。具体操作方法是模块化。模块是按功能来分的,所以也称功能块。在C+中称为一个函数,一个函数解决一个问题,即实现一个功能或一个操作。 在模块化的思想中已经出现了封装的概念,这个封装是把数据封装到模块中,即局部变量。这是很不彻底的,因为模块是功能的抽象,而数据则是具有其个性的,一但发生变化,抽象的功能模块就不再适用了。可维护性差成了制约结构化程序设计的瓶颈。 面向过程程序设计缺点的根源在于数据与数据处理分离。 4.2从面向过程到面向对象(阅读)结构化程序设计弱点: 当软件规模过大,采用结构化程序设计,其开发和维护就越来越难控制。其根本的原因就在于面向过程的结构化程序设计的方法与现实世界(包括主

12、观世界和客观世界)往往都不一致,结构化程序设计的思想往往很难贯彻到底。 对象概念:对象的概念是面向对象技术的核心所在。面向对象技术中的对象就是现实世界中某个具体的物理实体在计算机逻辑中的映射和体现。4.2从面向过程到面向对象(阅读)对象类计算机世界实体抽象类别现实世界客观世界抽象抽象实例化映射主观世界图4.3对象、实体与类现实世界中的实体可以抽象出类别的概念。对应于计算机世界就有一个类(class)的概念。面向对象是计算机世界模拟现实世界。图4.3表达了计算机世界与现实世界之间的对应关系。 4.2从面向过程到面向对象(阅读)对象、类与消息:面向对象程序设计模拟自然界认识和处理事物的方法,将数据

13、和对数据的操作方法放在一起,形成一个相对独立的整体对象(object),同类对象还可抽象出共性,形成类(class )。一个类中的数据通常只能通过本类提供的方法进行处理,这些方法成为该类与外部的接口。对象之间通过消息(message)进行通讯。4.2从面向过程到面向对象(阅读)属性行为表针旋钮其他机械机构调节旋钮对 象4.2从面向过程到面向对象(阅读)是一个抽象的概念,用来描述某一类对象所共有的、本质的属性和类行为。 类类的一个具体实现,称为实例手表 一块手表类 对象描述这类对象共有的、本质的属性和行为手表共有的属性(表针、旋钮、内部结构)和行为(调节旋钮)具体到一只圆形的或方形的手表4.2从

14、面向过程到面向对象(阅读)我们把对象之间产生相互作用所传递的信息称做消息。 消 息启 动发送消息接收并响应消息转 向4.2从面向过程到面向对象(阅读)封装性内外机械零件动作调节旋钮读表盘对象是一个封装体,在其中封装了该对象的属性和操作。通过限制对属性和操作的访问权限,可以将属性“隐藏”在对象内部,对外提供一定的接口,在对象之外只能通过接口对对象进行操作。C+通过建立数据类型类来支持封装和数据隐藏。封装性增加了对象的独立性,从而保证了数据的可靠性。一个定义完好的类可以作为独立模块使用。面向对象程序设计的特点:汽车客车货车小轿车大客车载货载人小,速度快大,速度慢4.2从面向过程到面向对象(阅读)继

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

16、英语、计算机、线性代数4.2从面向过程到面向对象(阅读)继承和多态性组合,可以生成很多相似但又独一无二的对象。继承性使得这些对象可以共享许多相似特性,而多态又使同一个操作对不同对象产生不同表现形式。这样不仅提高了程序设计的灵活性,而且减轻了分别设计的负担。4.3构造函数和析构函数数据成员多为私有的,要对它们进行初始化,必须用一个公有函数来进行。同时这个函数应该在且仅在定义对象时自动执行一次。称为:构造函数(constructor)4.3.1构造函数的定义与使用 4.3.2析构函数的定义 附录: 用UML类图描述类与对象构造函数特征:1.函数名与类名相同。2.构造函数无函数返回类型说明。注意是什

17、么也不写,也不可写void!3.在程序运行时,当新的对象被建立,该对象所属的类的构造函数自动被调用,在该对象生存期中也只调用这一次。4.构造函数可以重载。严格地讲,说明中可以有多个构造函数,它们由不同的参数表区分,系统在自动调用时按一般函数重载的规则选一个执行。4.3.1构造函数的定义与使用 4.3.1构造函数的定义与使用5.构造函数可以在类中定义,也可以在类外定义。 6. 如果类说明中没有给出构造函数,则C+编译器自动给出一个默认的构造函数: 类名(void) 但只要我们定义了一个构造函数,系统就不会自动生成默认的构造函数。 只要构造函数是无参的或各参数均有默认值的,C+编译器都认为是默认的

18、构造函数,并且默认的构造函数只能有一个 。 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;Amount=0; Total_value=0.0;默认的构造函数:CGoods()Name0=0 ; Pri

19、ce=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) ;调用的是第二个构造函数,参数为两个。CGoods Car3; 定义时调用不带参数的构造函数但是定义对象时不能加括号。例如:CGoods Car4();Car4()是不带参数的函数,它的返

20、回值是类CGoods的对象。【例4.1_1】完整商品类对象应用实例4.3.2析构函数的定义 析构函数(destructor)特征: 当一个对象的生命周期结束时,C+会自动调用析构函数(destructor)对该对象并进行善后工作,1.构函数名与类名相同,但在前面加上字符,如CGoods()。2.析构函数无函数返回类型,与构造函数在这方面是一样的。但析构函数不带任何参数。3. 一个类有一个也只有一个析构函数,这与构造函数不同。析构函数可以默认。4. 对象注销时,系统自动调用析构函数。【例4.2】定义一个矩形类用UML类图描述类与对象UML类图可以直观地表达类的成员、类与类之间的关系。基本类图类图

21、就是用图形来描述类,包括类名、数据成员和函数成员,以及各成员的访问限定特性。在UML中用矩形代表类,通常用两条横线将其分为三个部分,最上面是标题栏填类名,中间属性栏填类的数据成员,最下面是操作栏填函数成员。这三个部分只有类名是必须有的,其他为可选项。数据成员的表达方式访问限定特性名称重数:类型=默认值约束特征其中,访问限定特性包括public、private和protected三种,分别用“+”、“-”和“#”表示。名称后的方括号中的重数相当于C+中的数组定义。与C+不同,类型放在数据名称的后面,并加冒号。符号中的是可选项。用UML类图描述类与对象UML的类型并不与C+的类型完全一样,但在实际

22、应用时不必拘泥,可以用C+的类型代替。最后可以在花括号中加约束特征,如“只读”,对应C+的const,对于C+而言,该项不必填写。函数成员的表达方式访问限定特性名称(参数表):返回类型约束特征可以看出UML与C+的返回类型表示方法不同。参数表的表示方法也与C+不同,每一个参数表达方式为:方向名称 :类型=默认值方向表示参数是用于输入(in)、输出(out)或同时用于输入输出(inout)。对C+而言,该项不必填写。但使用Visio自动填写。用UML类图描述类与对象商品类CGoods类图 用UML类图描述类与对象对象图对象图的标题栏中书写方式为:对象名:类名多了对象名,多了冒号,多了下划线。对象

23、图中只有属性,而没有操作,这与操作为所有类对象共有,相一致。Cgoods类的对象Car对象图如下:用UML类图描述类与对象成员表述式的附加说明 可以在成员表述式的最前面加一个说明术语,用书名号(双尖括号)括住。如构造函数可加constructor,析构函数可加destructor。另外后面将会学到的友元函数可加friend,静态成员可加static等等4.4引用与复制构造函数 4.4.1 引用 4.4.2 复制构造函数 4.4.3 成员对象与构造函数 4. 4.1引用 引用的导入: 参数传递的传值方式在函数域中为参数重新分配内存,而把实参的数值传递到新分配的内存中。它的优点是有效避免函数的副作

24、用。 问题:如果要求改变实参的值,怎么办呢?如果实参是一个复杂的对象,重新分配内存会引起程序执行效率大大下降,怎么办呢? 有一种导出型数据类型引用(reference)可以解决上面的难题。引用又称别名(alias)。4.4.1引用 引用的定义: 引用是给一个已经定义的变量重新起一个别名,而不是定义一个新的变量,定义的格式为:类型 &引用变量名=已定义过的变量名;例如:double number ;double &newnum=number ;newnum是新定义的引用类型变量,它是变量number的别名。 引用主要用于函数之间的数据传递。4.4.1引用 newnum是变量number的别名,C

25、+系统不为引用类型变量分配内存空间。内存分配见下图:number称为引用newnum的关联变量。“&”(读作ampersand)在这里是引用的说明符。必须注意number和newnum都是double类型。如在程序中修改了newnum也就是修改了number,两位一体。注意:对数组只能引用数组元素,不能引用数组(数组名本身为地址)。4.4.1引用 【例4.4】引用作为函数的返回值一般函数返回值时,要生成一个临时变量作为返回值的副本,而用引用作为返回值时,不生成值的副本。【例4.5】 返回值为引用的函数作为左值(选读)【例4.3】引用作为函数的参数。采用引用调用时,将对实参进行操作。注意:采用引

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

27、d) strcpy (Name , cgd.Name); Price= cgd.price; Amount=cgd.Amount; Total_value=cgd.Total_value;注意:在成员函数中,参数为同类对象,其私有成员可以直接使用点操作符访问。4.4.2 复制构造函数 复制构造函数特征:1.复制构造函数的参数必须采用引用。* 在C+中按值传递一个参数时,会在函数中重新分配一块内存建立与参数同类型的变量或对象,再把参数的数据成员赋给新的变量或对象。在建立这个对象时,编译器就会自动为这个对象调用复制构造函数。如果其参数是真实的对象而不是引用,则又会引入新的一轮调用复制构造函数的过程

28、,出现了无穷递归。4.4.2 复制构造函数 2.系统会自动提供称为默认的按成员语义支持的复制构造函数,亦称为默认的按成员初始化。按成员作复制是通过依次复制每个数据成员实现的。赋值运算符“=”称默认的按成员复制赋值操作符(Copy Assignment Operator) ,同类对象之间可以用“=”直接复制 。3. 通常按成员语义支持已经足够。但在某些情况下,它对类与对象的安全性和处理的正确性还不够,这时就要求提供特殊的复制构造函数和复制赋值操作符的定义。4.4.2 复制构造函数 实例:CGood Car1(“夏利2000”,30,98000.00);/调用三个参数的构造函数CGood Car2

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

30、对象,再返回调用者。 因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。所谓return 对象,实际上是调用复制构造函数把该对象的值拷入临时对象。如果返回的是变量,处理过程类似,只是不调用构造函数。4.4.3 成员对象与构造函数聚合(aggregation ) : 类中的成员,除了成员数据和成员函数外,还有成员对象,即用其他类的对象作为类的成员。使用成员对象的技术称为聚合。成员对象是实体,系统不仅为它分配内存,而且要进行初始化。【例4.6】含有成员对象的类的构造函数4.

31、4.3 成员对象与构造函数含对象成员的构造函数:类名:构造函数名(参数总表):对象成员1(参数名表1),对象成员2(参数名表2),对象成员n(参数名表n)冒号后用逗号隔开的为要初始化的对象成员,附在后面的参数名表1,参数名表n依次为调用相应对象成员所属的构造函数时的实参表。这些表中的参数通常来自冒号前的参数总表,但没有类型说明 。 含对象成员的类对象的初始化时,首先依次自动调用各成员对象的构造函数,再执行该类对象自己的构造函数的函数体部分。各成员对象的构造函数调用的次序与类定义中说明的顺序一致,而与它们在构造函数成员初始化列表中的顺序无关。4.4.3 成员对象与构造函数 含对象成员的析构函数:

32、 因为析构函数没有参数,所以包含成员对象的类的析构函数形式上并无特殊之处。但是撤销该类对象时,会首先调用自己的析构函数,再调用成员对象的析构函数,调用次序与初始化时的次序相反。4.4.3 成员对象与构造函数构造函数另一格式:对于不含对象成员的类对象的初始化,也可以套用以上的格式,把部分需要直接赋初值的变量初始化写在冒号的右边:类名:构造函数名(参数表):变量1(初值1),变量n(初值n)当然也可以把一部分变量重新放回花括号中的函数体。冒号以后部分实际是函数体的一部分,所以在构造函数的声明中,冒号及冒号以后部分必须略去。4.4.3 成员对象与构造函数构造函数和析构函数的调用规则:1. 对全局定义

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

34、须定义一个函数,并通知C+编译器,当遇到该重载的运算符时调用此函数。这个函数叫做运算符重载函数,通常为类的成员函数。运算符重载函数定义:返回值类型 类名:operator重载的运算符(参数表) operator是关键字,它与重载的运算符一起构成函数名。因函数名的特殊性,C+编译器可以将这类函数识别出来。【例4.8】复数类,应用它进行复数运算细解运算符重载:复数类+的重载:Complex Complex:operator+(Complex c) /显式说明局部对象 Complex Temp(Real+c.Real , Image+c.Image) ; /注意:直接写对象c的私有成员,不用调c的公

35、有函数处理 return Temp ;编译器把表达式c2+c3解释为:c2.operator+(c3) ;函数c2.operator创建一个局部的Complex对象Temp,把出现在表达式中的两个Complex类对象c2和c3的实部之和及虚部之和暂存其内,然后把这个局部对象返回,赋给Complex类对象c(注意这里调用了复制构造函数生成一个作为返回值载体的无名临时对象)。参见图4.8。4.5运算符的重载Temp.Real=Real+ c2.Real;Temp.Image=Image+ c3.Image;c=return(Temp);RealImagec3.Realc3.Image=+局部对象T

36、emp当前对象c2对象c3图4.8 显式说明临时对象的“+”运算符执行过程隐式返回计算结果: 省略局部的Complex对象TempComplex Complex:operator+(double d) return Complex(Real+d , Image);/隐式说明局部对象在return后面跟的表达式中调用的是类的构造函数,它为新建的作为返回值载体的无名对象进行初始化。 4.5运算符的重载说明:Complex Complex:operator+(Complex c) Complex Temp(Real+c.Real , Image+c.Image) ; return Temp ;当成员

37、函数的参数为同一类(class)的对象或它的引用,在函数体内使用参数对象的私有数据成员时,可用对象名加成员访问操作符点号进行。从逻辑上讲,每个对象有自己的成员函数,访问同类其他对象的私有数据成员应通过该对象的公有函数,不能直接访问。但在物理上只有一个成员函数代码,所以直接访问是合理的。仅在成员函数中可以这样做。4.5运算符的重载引用作为参数:Complex Complex:operator+(const Complex &c) return Complex(real+c.real , Image+c.Image) ;注意:参数采用对象的引用而不是对象本身,调用时不再重新分配内存建立一个复制的对

38、象,函数效率会更高。而在引用形式参数类型说明前加const关键字,表示被引用的实参是不可改变的,如程序员不当心在函数体中重新赋值了被引用的实参, C+编译器会认为出错。4.5运算符的重载const引用进一步说明:引用在内部存放的是被引用对象的地址,不可寻址的值是不能引用的;当引用作为形参时,实参也不能使用不可寻址的值,更不可能进行类型转换(如:实数转换为整数)。但是const引用不同,它是只读的,为了绝对保证不会发生误改,编译器实现const引用时,生成一个临时对象,引用实际上指向该临时对象,但用户不能访问它。所以const引用可以实现不可寻址的值(包括字面常量)的引用。例如:double d

39、val=1024; const int &ri=dval;是正确的,编译器将其转换为:double dval=1024; int temp=dval; const int &ri=temp;因有临时对象,引用和类型转换都实现了。当const引用作为形参时,实参也能使用不可寻址的值,并能进行类型转换。4.5运算符的重载默认的复数复制赋值操作符:函数声明:Complex &Complex:operator = (Complex& c);这种默认的赋值操作格式对所有类都是固定的,对复数是合适的。默认的赋值操作返回对象本身的引用,它可以进行连续赋值。如:a=b=c=d ;本例中重载的赋值运算符“=”取

40、代了默认的赋值操作,返回一个复数临时变量,尽管该复数生命期仅在使用赋值号的表达式(如a=b=c)中,却也能进行连续赋值。但它的执行效率和代码简洁性较差。重载的运算符“+=”标准算法声明是:Complex& Complex:operator +=(Complex & com);4.5运算符的重载2.当用类的成员函数实现运算符的重载时,运算符重载函数的参数(当为双目运算符时)为一个或(当为单目运算符时)没有。运算符的左操作数一定是对象,因为重载的运算符是该对象的成员函数,而右操作数是该函数的参数。 3.单目运算符“+”和“-”存在前置与后置问题。前置“+”格式为: 返回类型 类名:operator

41、+() 而后置“+”格式为: 返回类型 类名:operator+(int) 后置“+”中的参数int仅用作区分。小结:1. 运算符重载函数的函数名必须为关键字operator加一个合法的运算符。在调用该函数时,将右操作数作为函数的实参。4.5运算符的重载运算符运算符名称禁止重载的理由? :三目条件运算符C+中没有定义三目运算符的语法.和.*成员与成员指针操作符为保证成员操作符对成员访问的安全性:作用域操作符该操作符右操作数不是表达式sizeof类型字长操作符该操作符的操作数为类型名,不是表达式表4.1 C+中不允许重载的运算符4. C+中只有极少数的运算符不允许重载。 4.5运算符的重载问题:

42、例5.7中:c=c+d;语句,改为c=d+c;因为d不是Complex的对象,C+编译器将无法找到合适的重载的“+”运算符对应的函数,最终给出出错信息。怎样解决? 4.6友元 在C+中友元(friend)函数允许在类外访问该类中的任何成员,就象成员函数一样。友元函数用关键字friend说明。上节答案:用友元函数重载运算符“+”,可以实现c=d+c;4.6友元class Complex friend Complex operator + (double,Complex); ; /opration+说明为类Complex类的友元函数Complex operator + (double d , Co

43、mplex c) return Complex(d+c.Real , c.Image) ; /friend只用于类说明中,定义时不加friend/注意友元不是成员函数,但可以直接访问私有成员int main(void)c=d+c1; c.print();return 0;解释:d+c被C+编译器解释为:operator+(d,c)4.6友元友元函数重载运算符形式:+有三种形式。另两个的声明为:friend Complex operator +(Complex , Complex ) ;friend Complex operator + (Complex , double ) ;涵盖实数与复数,

44、复数与复数,复数与实数相加三种情况。可以仅使用友元函数friend complex operator +(complex , complex) ;实数被默认的构造函数强制转换为虚部为零的复数。4.6友元比较:友元函数可以有两个参数,而对应的成员函数只有一个参数,所以友元函数的使用可以更灵活、更方便。 图4.9 友元函数operator+执行过程内存分配 d+c1被解释为:operator+(complex(d) , c1)注意:传值,在函数内是建立了两个复数对象,而把实参的值传进去,进行运算。参见图4.9。4.6友元改进: Operator+友元函数的形参可使用引用类型变量friend Com

45、plex operator+(const Complex & c1, const Complex & c2) 采用Complex对象的引用而不是对象本身,调用时不再重新分配内存建立一个复制的对象,函数效率会更高。加const,实参只读,可防止实参被修改。 这里const不可不加,否则需重载的运算符+的三个函数一个不可少,因为形参为简单的引用时,身为左值的实参不能被转换,【例4.8_1】中的c=c+d和c=d+c编译时都通不过。加了const,隐式的实数到复数的转换将在一份副本上进行,与不用引用时相似。【例4.8_1】 用友元函数重载运算符4.6友元单目运算符前“+”的成员函数重载方式如下:Co

46、mplex Complex:operator+() return Complex (+Real , +Image) ;采用成员函数方式重载与使用都很方便。友元函数重载后置“+”如下:friend Complex operator+(Complex & c , int) /注意友元方式与前者的区别 return Complex(c.Real+ , c.Image+) ;采用引用类型,后“+”是直接施加于实参。否则施加于副本,而实参不变。 注意:复制赋值运算符(=)重载必须为成员函数,不可为友元函数。因为默认的复制赋值运算符(=)是成员函数,友元函数不能取代它。友元函数注意点: 1.友元函数不是类

47、的成员函数,在函数体中访问对象的成员,必须用对象名加运算符“.”加对象成员名。但友元函数可以访问类中的所有成员,一般函数只能访问类中的公有成员。 2.友元函数不受类中的访问权限关键字限制,可以把它放在类的公有、私有、保护部分,但结果一样。 3.某类的友元函数的作用域并非该类域。如果该友元函数是另一类的成员函数,则其作用域为另一类的类域,否则与一般函数相同。友元类:整个类可以是另一个类的友元。友元类的每个成员函数都是另一个类的友元函数,都可访问另一个类中的保护或私有数据成员。定义方法如下:class A friend class B; /声明B为A的友元类 ;4.7 静态成员 由关键字stati

48、c修饰说明的类成员,成为静态类成员(static class member)。但与函数中的静态变量有明显差异。类的静态成员为其所有对象共享,不管有多少对象,静态成员只有一份存于公用内存中。4.7.1 静态数据 4.7.2 静态函数成员(选读) 4.7.1 静态数据静态数据成员定义与使用: 在类定义中,用关键字static修饰的数据成员为静态数据成员。 该类所有对象共享由系统为静态成员分配的一个存储空间,而这个存储空间是在编译时分配的,在定义对象时不再为静态成员分配空间。 静态数据是该类所有对象所共有的,可提供同一类的所有对象之间信息交换的捷径。 静态数据成员属于整个类,使用时可用以下格式:类名

49、:静态数据成员名 4.7.1 静态数据【例4.9】用静态数据成员计算由同一类建立的对象的数量执行程序后输出:对象数量=1 /a0构造函数产生对象数量=2 /a1构造函数产生对象数量=3 /a2构造函数产生对象数量=2 /a2析构函数产生对象数量=1 /a1析构函数产生对象数量=0 /a0析构函数产生4.7.2 静态函数成员(选读)静态函数成员的使用: 函数成员说明为静态,将与该类的不同对象无关。静态函数成员的调用,在对象之外可以采用下面的方式:类名:函数名(对象名,参数表);任一类对象名.函数名(对象名,参数表); 静态函数成员多为公有的。在例4.8中的复数类中的函数成员print(), 改为

50、静态的则可如下表达:static void print(complex & ob) cout”Real=”ob.Realt ”Image=”ob.Imagen ;增加参数是为了告诉C+系统应取哪一个对象的数据,否则只能处理该类的静态数据。4.8 结构结构类型的引入: 在C+中结构(structure)与类几乎是完全一样的类型,差别仅仅在于默认情况下结构的成员为公有的。 在C语言阶段,结构就已存在,但它只有公有的数据成员。正因为如此,C+程序员仍然使用结构,但是只为结构安排公有的数据成员。因为这样程序更易读易懂。在程序设计中常把结构类型的数据作为类的数据成员。 C风格的定义:struct 结构类

51、型名 类型名 变量1;类型名 变量2;; /最后的分号不可少4.8 结构实例:struct inventory /库存货物char description15 ; /货物名称char no10 ; /货号int quantity ; /库存数量double cost ; /成本double retail ; ; /零售价格struct employee /员工char name27 ; /员工姓名char address30 ; /家庭住址long int zip ; /邮政编码long int telenum ; /联络电话double salary ; ; /工资4.8 结构变量定义与初始

52、化: 结构是一种派生数据类型,定义结构时并不分配存储空间,只有定义了结构类型的变量,编译系统才为结构变量分配存储空间。定义变量方法如下:inventory car , motor ; 初始化是用花括号中顺序填入结构中的(公有数据)成员的初始值完成的:employee emp1=“朱明”,“四牌楼2号”,210096,3792666,2430.0,emp2=“沈俊”,“丁家桥15号”,210009,3273389,1920.0; 结构变量的访问与类一样,可使用成员访问操作符之一:点操作符,对成员一个个进行:变量名 . 成员名4.8 结构结构类型使用说明: (1) 与同类的对象之间可以复制一样,同

53、结构类型的变量之间也可以作为整体相互赋值(复制)。(2)结构变量也可以作为函数的参数和返回值,结构作为参数可以按值(复制)进行传递的,也可以按引用传递。 (3)在程序文件中强烈推荐将结构类型的定义放在所有函数的外面,这样程序文件中的各个函数可以按需要在各个函数中声明局部的结构变量。在各函数中定义结构类型,即使两个函数中定义的完全一样,系统也完全认为是两种结构类型。 4.8 结构(4)结构可以嵌套:struct mailchar address30 ; /地址long int zip ; /邮政编码long int telenum ; /电话号码struct employeechar name2

54、5 ; /员工姓名mail addinfo ; /结构作为成员,嵌套double salary ; ; /工资用连续点号来访问结构变量的结构成员中的成员:employee emp1=“朱明”,“四牌楼2号”,210096, 3792666,2430.0;coutemp1.addinfo.telenum ;输出为3792666。4.8 结构联合(选读): union 共同体变量名类型 成员名1;类型 成员名2;; 联合(union)与结构的区别是:结构变量的各成员同时被分配了各自独立的内存区,而联合变量的各个成员的存储开始地址都相同,所以在任一时刻联合变量只能存储一个成员。 系统为联合变量分配空

55、间时按需要最大存储量的成员大小分配内存空间。 联合被称为一种特殊的类(它因编译器不能知道成员的类型,而没有构造函数和析构函数,所以称特殊的类)。 4.8 结构用途:联合的典型用途是按不同方式访问同一块内存。例如:union num int k; char ch2;a;图4.10 联合变量a内存分配系统为变量a分配了4个字节的空间。如果以ch1记楼层号,以ch0记同一楼层的房间号,如15楼8号房间则可赋值:a.ch1=15; a.ch0=8; 见图4.10。如果需要把所有房间顺序排号,则可用a.k来读取一个整型数(15*256+8),1楼1号房间排在第一,楼层越高、同一层房间号越大的房间排得越后

56、。用UML类图表达类间关系类间关系 类与类的关系包括:关联、聚合、组合、泛化及各种形式的依赖关系等等。关联(association)某些真实世界的实体存在明显的联系,如驾驶员与汽车、书籍与图书馆、引擎与交通工具。如果这样的实体在程序中用类来表示,则它们通过关联关系来联系。在UML图中用实线相连接。用UML类图表达类间关系 类的关联关系实际是类对象而不是类本身之间存在一些关系。典型的情况有:如果一个类对象调用另一个类对象的一个操作,则这两个类是相关联的;如果一个类的某属性为另一个类的对象,则两者也可能存在关联关系。上面的例子都属使用关系。适航性(navigability) 关联具有方向性或称适航

57、性(navigability),采用由客户指向提供者的开箭头表示,如类对象甲调用类对象乙的一个操作,则由甲指向乙的单向开箭头表示。如果甲乙互相调用,用双向箭头表示,称双向(bidirectional)关联关系。适航性箭头是可选的。用UML类图表达类间关系多重性(multiplicity)是UML类图中的一个特性,例如一个交通工具可以有0到n个引擎,可以在这两者连线的交通工具类对象一侧标示1,在引擎类对象一侧标示0.n,称为重数。用术语表示是每个引擎对象与1个交通工具对象关联,而每个交通工具与0到n个引擎相关联。标记说明*任何数量的对象(包括0)1一个对象nn个对象(n为整数,下同)0.10或1

58、个对象(即关联时可选的)n.m最少为n个对象,最多为m个对象1,3离散的结合(这里是1个或3个)用UML类图表达类间关系例:图书管理系统中有一个Loan类,它将Reader、Librarian和Item三个类相关联,即将整个借阅过程构成整体。用UML类图表达类间关系聚合(aggregation)聚合是一种关联关系,它指明一个聚集和组成部分之间的整体与部分的关系。聚合是一种拥有(has a)关系。图书馆拥有图书,账单拥有明细条目。聚合关系的表示与关联关系相似,仅仅连线在整体一侧有一个中空的菱形,取代了开箭头。组合(composition)组合是一种更简单的聚合,它要求部分只属于一个整体,并且部分

59、的生命期与整体的生命期相同,具有更强的拥有关系。C+中的一个类对象具有成员对象,体现的就是严格的组合关系。其表示方法与聚合基本相同,仅仅菱形是实心的。用UML类图表达类间关系 在现实世界中聚合和组合都是存在的。 如科研活动中的课题组包含许多成员,但每个成员都可以同时是另一个课题组的成员,这对应聚合,不对应组合,部分可以参加多个整体,具有共享性。 而汽车与发动机对应组合,是一种特殊的聚合,无共享性。应该说C+在这方面还不能很好反应现实世界。 图书馆类包括6组成员对象,在C+中他们都是对象数组。这里用组合来描述应该更准确一些。在C+中表达聚合还没有一个好办法。用UML类图表达类间关系4.9名字空间

60、域和类域 (选读) 在C+中支持三种域:局部域、名字空间域和类域。名字空间域声明:名字空间域相当于一个更加灵活的文件域(全局域),可以用花括号把文件的一部分括起来,并以关键字namespace开头给它起一个名字:namespace ns1 float a,b,c; fun1() 花括号括起来的部分称声明块。声明块中可以包括:类、变量(带有初始化)、函数(带有定义)等。4.9名字空间域和类域 (选读)名字空间域访问: 在域外使用域内的成员时,需加上名字空间名作为前缀,后面加上域操作符“:” 。这里添加了名字空间名称的成员名被称为限定修饰名(qualified name)。如:ns1:a, ns1

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论