第四章-类与对象_第1页
第四章-类与对象_第2页
第四章-类与对象_第3页
第四章-类与对象_第4页
第四章-类与对象_第5页
已阅读5页,还剩113页未读 继续免费阅读

下载本文档

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

文档简介

第四章类与对象,封装(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构造函数和析构函数,4.1类与对象的基本概念,4.1.3对象的创建与使用,4.1.1C+类的定义,4.1.2成员函数的定义,4.1.1C+类的定义,类的引入:类是一种数据类型。描述客观事物必须用不同的数据类型来描述不同的方面。如商品:商品名称(用字符串描述),该商品数量(用整型数描述),该商品单价(用浮点数描述),该商品总价(用浮点数描述)。这里用了属于三种不同数据类型的四个数据成员(datamember)来描述一种商品。,4.1.1C+类的定义,类的表述:classCGoodspublic:charName21;intAmount;floatPrice;floatTotal_value;,4.1.1C+类的定义,访问限定符(accessspecifier):public(公共的)说明的成员能从外部进行访问。private(私有的)和protected(保护的)说明的成员不能从外部进行访问。每种说明符可在类体中使用多次。访问限定符的作用域是从该说明符出现开始到下一个说明符之前或类体结束之前结束。如果在类体起始点无访问说明符,系统默认定义为私有(private)。访问限定符private(私有的)和protected(保护的)体现了类具有封装性(Encapsulation)。,4.1.1C+类的定义,类的定义:class类名private:成员表1;public:成员表2;protected:成员表3;其中“class类名”称为类头(classhead)。花括号中的部分称为类体(classbody),类体中定义了类成员表(classmemberlist),包含数据和函数。,4.1.1C+类的定义,成员函数(memberfunction):classCGoodsprivate:charName21;intAmount;floatPrice;floatTotal_value;public:voidRegisterGoods(char,int,float);/输入数据voidCountTotal(void);/计算商品总价值voidGetName(char);/读取商品名intGetAmount(void);/读取商品数量floatGetPrice(void);/读取商品单价floatGetTotal_value(void);/读取商品总价值,4.1.1C+类的定义,封装:类把数据(事物的属性)和函数(事物的行为操作)封装为一个整体。接口:通常数据成员被说明成私有的,函数成员被说明成公有的;从外部对数据成员进行操作,只能通过公有函数来完成,数据受到了良好的保护,不易受副作用的影响。公有函数集定义了类的接口(interface)。成员函数可以直接使用类定义中的任一成员,可以处理数据成员,也可调用函数成员。注意:类是一种数据类型,定义时系统不为类分配存储空间,所以不能对类的数据成员初始化。类中的任何数据成员也不能使用关键字extern、auto或register限定其存储类型。,4.1.2成员函数的定义,函数定义:通常在类定义中,成员函数仅作声明。函数定义通常在类的说明之后进行,其格式如下:返回值类型类名:函数名(参数表)/函数体其中运算符“:”称为作用域解析运算符(scoperesolutionoperator),它指出该函数是属于哪一个类的成员函数。,类CGoods的函数定义,voidCGoods:RegisterGoods(charname,intamount,floatprice)strcpy(Name,name);Amount=amount;Price=price;voidCGoods:CountTotal(void)Total_value=Price*Amount;voidCGoods:GetName(charname)strcpy(name,Name);intCGoods:GetAmount(void)return(Amount);floatCGoods:GetPrice(dvoi)return(Price);floatCGoods:GetTotal_value(void)return(Total_value);,定义对象:对象是类的实例(instance)。定义一种数据类型只是告诉编译系统该数据类型的构造,并没有预定内存。类只是一个样板,以此样板可以在内存中开辟出同样结构的实例对象。格式如下:CGoodsCar;这个定义创建了CGoods类的一个对象Car,同时为它分配了属于它自己的存储块,用来存放数据和对这些数据实施操作的成员函数(代码)。对象只在定义它的域中有效。,4.1.3对象的创建与使用,对象存储:,图4.1各对象完全独立地安排内存的方案,图4.1是系统为每一个对象分配了全套的内存。数据区安放成员数据,代码区安放成员函数。注意:区别同一个类的各个不同的对象的属性是由数据成员决定的,不同对象的数据成员的内容是不一样的;而行为(操作)是用函数来描述的,这些操作的代码对所有对象都是一样的。,图4.2各对象的代码区共用的方案,图4.2仅为每个对象分配一个数据区,代码区(放成员函数的区域)为各对象类共用。图4.1对应的是在类说明中定义函数,而图4.2对应的是在类说明外部定义函数。,4.1.3对象的创建与使用,内联函数:使用关键字inline,系统自动采用内联扩展方法实现,每个对象都有该函数一份独立的代码。如RegisterGoods()函数可定义为:inlinevoidCGoods:RegisterGoods(charname,intamount,floatprice)strcpy(Name,name);Amount=amount;Price=price;则每个对象都有RegisterGoods()函数一份独立的代码。注意:inline只是一个建议,最后由编译器决定是否执行。对象的存储方式是物理的,这是由计算机来完成的,它并不影响类在逻辑上的封装性。,4.1.3对象的创建与使用,对象使用规则:只要在对象名后加点号(点操作符,成员访问运算符(memberaccessoprator)之一),再加成员数据或成员函数名就可以了。但是这些成员必须是公有的成员,只有公有成员才能在对象的外面对它进行访问。,【例4.1】商品类对象应用实例,【例4.1】中对象car的4个数据成员全是私有的,如写:car.Name;car.Amount;car.Price;car.Total_value;是错误的,必须用对象car所带的公有函数进行访问。,4.2从面向过程到面向对象,结构化程序设计特点:采用的是“自顶向下,逐步细化(divideandconquer,stepwiserefinement)”的思想。具体操作方法是模块化。模块是按功能来分的,所以也称功能块。在C+中称为一个函数,一个函数解决一个问题,即实现一个功能或一个操作。,在模块化的思想中已经出现了封装的概念,这个封装是把数据封装到模块中,即局部变量。这是很不彻底的,因为模块是功能的抽象,而数据则是具有其个性的,一但发生变化,抽象的功能模块就不再适用了。可维护性差成了制约结构化程序设计的瓶颈。,4.2从面向过程到面向对象,对象概念:对象的概念是面向对象技术的核心所在。面向对象技术中的对象就是现实世界中某个具体的物理实体在计算机逻辑中的映射和体现。,结构化程序设计弱点:当软件规模过大,采用结构化程序设计,其开发和维护就越来越难控制。其根本的原因就在于面向过程的结构化程序设计的方法与现实世界(包括主观世界和客观世界)往往都不一致,结构化程序设计的思想往往很难贯彻到底。,4.2从面向过程到面向对象,图4.3对象、实体与类,现实世界中的实体可以抽象出类别的概念。对应于计算机世界就有一个类(class)的概念。面向对象是计算机世界模拟现实世界。图4.3表达了计算机世界与现实世界之间的对应关系。,4.3构造函数和析构函数,数据成员多为私有的,要对它们进行初始化,必须用一个公有函数来进行。同时这个函数应该在且仅在定义对象时自动执行一次。称为:构造函数(constructor),4.3.1构造函数的定义与使用,4.3.2析构函数的定义,构造函数特征:1.函数名与类名相同。2.构造函数无函数返回类型说明。注意是什么也不写,也不可写void!3.在程序运行时,当新的对象被建立,该对象所属的类的构造函数自动被调用,在该对象生存期中也只调用这一次。4.构造函数可以重载。严格地讲,说明中可以有多个构造函数,它们由不同的参数表区分,系统在自动调用时按一般函数重载的规则选一个执行。,4.3.1构造函数的定义与使用,4.3.1构造函数的定义与使用,5.构造函数可以在类中定义,也可以在类外定义。6.如果类说明中没有给出构造函数,则C+编译器自动给出一个默认的构造函数:类名(void)但只要我们定义了一个构造函数,系统就不会自动生成默认的构造函数。只要构造函数是无参的或各参数均有默认值的,C+编译器都认为是默认的构造函数,并且默认的构造函数只能有一个。,4.3.1构造函数的定义与使用,CGoods的构造函数:三参数:Cgoods(char*name,intamount,floatprice)strcpy(Name,name);Amount=amount;Price=price;Total_value=price*amount;两参数:货名和单价,Cgoods(char*name,floatprice)strcpy(Name,name);Price=price;Amount=0;Total_value=0.0;默认的构造函数:CGoods()Name0=0;Price=0.0;Amount=0;Total_value=0.0;这三个构造函数同时被说明(重载)。,4.3.1构造函数的定义与使用,构造函数应用:CGoodsCar1(“夏利2000”,30,98000.0);调用了CGoods中的第一个构造函数,等效于:CGoodsCar1=CGoods(“夏利2000”,30,98000.0);,CGoodsCar2(“桑塔那2000”,164000.0);调用的是第二个构造函数,参数为两个。,CGoodsCar3;定义时调用不带参数的构造函数但是定义对象时不能加括号。例如:CGoodsCar4();Car4()是不带参数的函数,它的返回值是类CGoods的对象。,【例4.1_1】完整商品类对象应用实例,【例4.1_1】完整商品类对象应用实例,classCGoodsprivate:charName21;intAmount;floatPrice;floatTotal_value;public:CGoods();CGoods(char,int,float);CGoods(char,float);voidRegisterGoods(char,int,float);voidCountTotal(void);voidGetName(char);intGetAmount(void);floatGetPrice(void);floatGetTotal_value(void);,【例4.1_1】完整商品类对象应用实例,intmain()charstring21=0;CGoodsCar1(夏利2000,30,98000.0);CGoodsCar2(桑塔那2000,164000.0);Car1.GetName(string);/string赋值car.Namecoutsetw(20)stringsetw(5)Car1.GetAmount();coutsetw(10)Car1.GetPrice()setw(20)Car1.GetTotal_value()endl;Car2.GetName(string);/string赋值car.Namecoutsetw(20)stringsetw(5)Car2.GetAmount();coutsetw(10)Car2.GetPrice()setw(20)Car2.GetTotal_value()endl;return0;,4.3.2析构函数的定义,析构函数(destructor)特征:当一个对象的生命周期结束时,C+会自动调用析构函数(destructor)对该对象并进行善后工作,1.构函数名与类名相同,但在前面加上字符,如CGoods()。2.析构函数无函数返回类型,与构造函数在这方面是一样的。但析构函数不带任何参数。3.一个类有一个也只有一个析构函数,这与构造函数不同。析构函数可以默认。4.对象注销时,系统自动调用析构函数。,【例4.2】定义一个矩形类,【例4.2】矩形类。要确定一个矩形(四边都是水平或垂直方向,不能倾斜),只要确定其左上角和右下角的x和y坐标即可,即左右上下四个边界值。classRectangleintleft,top,right,bottom;public:Rectangle(int=0,int=0,int=0,int=0);/默认构造函数必须在此指定默认实参Rectangle();/析构函数,在此函数体为空voidAssign(int,int,int,int);voidSetLeft(intt)left=t;voidSetRight(intt)right=t;voidSetTop(intt)top=t;voidSetBottom(intt)bottom=t;voidShow();,Rectangle:Rectangle(intl,intt,intr,intb)left=l;top=t;right=r;bottom=b;voidRectangle:Assign(intl,intt,intr,intb)left=l;top=t;right=r;bottom=b;voidRectangle:Show()cout”left-toppointis(”left”,”top”)”n;cout”right-bottompointis(”right”,”bottom”)”n;,#includeusingnamespacestd;#include“rect.h”intmain()Rectanglerect;rect.Show();rect.Assign(100,200,300,400);rect.Show();Rectanglerect1(0,0,200,200);rect1.Show();Rectanglerect2(rect1);rect2.Show();return0;,练习:构造函数与析构函数的调用过程#includeclassApublic:A();A(inti,intj);A()coutDestructor.n;voidprint();private:inta,b;A:A()a=b=10;coutDefaultconstructor.n;A:A(inti,intj)a=i,b=j;coutConstructor.n;voidA:print()couta=a,b=bendl;voidmain()Am;An(15,18);m.print();n.print();,4.4引用与复制构造函数,4.4.1引用,4.4.2复制构造函数,4.4.3成员对象与构造函数,4.4.1引用,引用的导入:参数传递的传值方式在函数域中为参数重新分配内存,再把实参的数值传递到新分配的内存中。它的优点是有效避免函数的副作用。问题:如果要求改变实参的值,怎么办呢?如果实参是一个复杂的对象,重新分配内存会引起程序执行效率大大下降,怎么办呢?有一种导出型数据类型引用(reference)可以解决上面的难题。引用又称别名(alias)。,4.4.1引用,引用的定义:引用是给一个已经定义的变量重新起一个别名,而不是定义一个新的变量,定义的格式为:类型newnum是新定义的引用类型变量,它是变量number的别名。引用主要用于函数之间的数据传递。,4.4.1引用,newnum是变量number的别名,C+系统不为引用类型变量分配内存空间。内存分配见下图:,number称为引用newnum的关联变量,必须注意number和newnum都是double类型。如在程序中修改了newnum也就是修改了number,两位一体。,注意:对数组只能引用数组元素,不能引用数组(数组名本身为地址)。,【例4.3】引用作为形参,Xy,d1d2temp,1.414,2.718,1.414,voidswap(double,图4.5参数d1、d2为引用时内存分配示意,【例4.4】引用作为函数的返回值一般函数返回值时,要生成一个临时变量作为返回值的副本,而用引用作为返回值时,不生成值的副本。,注意:采用引用返回方式时,返回的不能是函数中的局部变量,这时返回的局部变量地址已经失效。引用方式返回最常用的是由引用参数传递过来的变量(见例4.5),其次是全局变量,这样返回的变量地址是有效的。,【例4.4】引用作为返回值,【例4.4】采用不同返回方式的求正方形面积函数的比较。doubletemp;/全局变量doublefsqr1(doublea)temp=a*a;returntemp;double运行结果为:x=30.25y=30.25运行结果一样,但在内存中的活动却不同。,【例4.4】引用作为返回值,图4.6普通返回,图4.7引用返回,【例4.5】返回值为引用的函数作为左值,【例4.4】统计学生成绩,分数在80分以上的为A类,60分以上,80分以下的为B类,60分以下为C类。int,4.4.2复制构造函数,复制构造函数引入:同一个类的对象在内存中有完全相同的结构,如果作为一个整体进行复制是完全可行的。这个复制过程只需要复制数据成员,而函数成员是共用的(只有一份代码)。在建立对象时可用同一类的另一个对象来初始化该对象,这时所用的构造函数称为复制构造函数(CopyConstructor)。CGoods类,复制构造函数为:CGoods(CGoods,例如:#includeusingnamespacestd;#includeusingnamespacestd;classCApublic:CA(intb)a=b;voidShow()coutaendl;private:inta;intmain()CAA(100);CAB=A;B.Show();return0;,CAB(A);,思考:将构造函数访问权限改为private结果如何?,#includeusingnamespacestd;classCApublic:CA(intb)a=b;CA(CA,intmain()CAA(100);CAB=A;B.Show();return0;,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:一个对象以值传递的方式传入函数体一个对象以值传递的方式从函数返回一个对象需要通过另外一个对象进行初始化。,4.4.2复制构造函数,复制构造函数特征:1.复制构造函数的参数必须采用引用。*在C+中按值传递一个参数时,会在函数中重新分配一块内存建立与参数同类型的变量或对象,再把参数的数据成员赋给新的变量或对象。在建立这个对象时,编译器就会自动为这个对象调用复制构造函数。如果其参数是真实的对象而不是引用,则又会引入新的一轮调用复制构造函数的过程,出现了无穷递归。,4.4.2复制构造函数,2.系统会自动提供称为默认的按成员语义支持的复制构造函数,亦称为默认的按成员初始化。按成员作复制是通过依次复制每个数据成员实现的。赋值运算符“=”称默认的按成员复制赋值操作符(CopyAssignmentOperator),同类对象之间可以用“=”直接复制。3.通常按成员语义支持已经足够。但在某些情况下,它对类与对象的安全性和处理的正确性还不够,这时就要求提供特殊的复制构造函数和复制赋值操作符的定义。,4.4.2复制构造函数,隐含的复制构造函数使用:当函数的形参是类对象,调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参复制到新的对象中。2.当函数的返回值是类对象,函数执行完成返回调用者时使用。理由也是要建立一个临时对象,再返回调用者。因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。所谓return对象,实际上是调用复制构造函数把该对象的值拷入临时对象。如果返回的是变量,处理过程类似,只是不调用构造函数。,对于一个类X,如果一个构造函数的第一个参数是下列之一:a)Xpublic:studentID(longid=0)value=id;cout赋给学生的学号:valueendl;studentID()cout删除学号:valueendl;classstudentprivate:studentIDid;charname20;public:student(charsname=noname,longsid=0):id(sid)strcpy(name,sname);cout“学生名:”nameendl;,这样运行结果为:赋给学生的学号:08002132学生名:朱明删去学生名:朱明删去学号:08002132,【例4.6】含有成员对象的类的构造函数,studentID()cout“删除学生名:nameendl;intmain()studentss(朱明,82020132);return0;,4.4.3成员对象与构造函数,构造函数另一格式:对于不含对象成员的类对象的初始化,也可以套用以上的格式,把部分需要直接赋初值的变量初始化写在冒号的右边:类名:构造函数名(参数表):变量1(初值1),变量n(初值n)当然也可以把一部分变量重新放回花括号中的函数体。冒号以后部分实际是函数体的一部分,所以在构造函数的声明中,冒号及冒号以后部分必须略去。,4.4.3成员对象与构造函数,构造函数和析构函数的调用规则:1.对全局定义的对象,当程序进入入口函数main之前对象就已经定义,那时要调用构造函数。整个程序结束时调用析构函数。2.对于局部定义的对象,每当程序控制流到达该对象定义处时,调用构造函数。当程序控制走出该局部域时,则调用析构函数。3.对于静态局部定义的对象,在程序控制首次到达该对象定义处时,调用构造函数。当整个程序结束时调用析构函数。,4.4.3成员对象与构造函数,在正确定义了构造函数和析构函数的前提下,在一个健康的程序中,每个创建的对象必然有一个而且只有一个撤消动作。,注意:先建立的对象后撤销。,【例4.7】演示对象创建和撤消的对应关系,【例4.7】演示对象创建和撤消的对应关系,本例目的是总结一下语法,请注意各函数输出的标志:classcomplexprivate:doublereal,image;public:complex()/默认的构造函数real=0.0;image=0.0;coutInitializing00endl;complex(doubler,doublei=0.0)/带参数的构造函数real=r;image=i;coutInitializingrtiendl;complex(complex,【例4.7】演示对象创建和撤消的对应关系,voidassign(complexcom)real=com.real;/先建立临时对象comimage=com.image;voidprint()coutreal+imageiendl;inlinecomplex:complex(complex,【例4.7】演示对象创建和撤消的对应关系,complexfun(complexcom)coutEnteringfunctionendl;global.assign(com);coutExitingfunctionendl;returnglobal;complexglobal;/全局对象首先建立intmain()coutEnteringmainendl;complexcom1,com2(5.6,7.5);complexcom3=com1;com3.print();global.print();com1=fun(com2);com1.print();coutExitingmainendl;return0;,【例4.7】演示对象创建和撤消的对应关系,运行结果:Initializing00/全局对象global建立,调默认的构造函数Enteringmain/进入入口函数mainInitializing00/用默认的构造函数建立com1Initializing5.67.5/用带参数的构造函数建立com2Copy00/用复制的构造函数建立com30+0i/打印com30+0i/打印globalCopy5.67.5/调用全局函数fun(),调用复制构造函数建立临时对象comEnteringfunction/进入全局函数fun()Copy5.67.5/进入global.assign(),/调用复制构造函数建立临时对象新com,【例4.7】演示对象创建和撤消的对应关系,Destructor/退出global.assign(),调用析构函数,清新comExitingfunction/将退出fun()Copy5.67.5/返回对象时调用复制构造函数建立临时对象Destructor/退出fun(),调用析构函数,清fun()的comDestructor/返回的临时对象赋给com1后析构5.6+7.5i/打印com1Exitmain/将退出入口函数mainDestructor/退出入口函数前,调用析构函数,清com3Destructor/退出入口函数前,调用析构函数,清com2Destructor/退出入口函数前,调用析构函数,清com1Destructor/退出入口函数前,调用析构函数,清global本例运行结果应与程序对比,看看程序运行的细节。,4.5运算符的重载,运算符重载的概念:运算符的重载是特殊的函数重载,必须定义一个函数,并通知C+编译器,当遇到该重载的运算符时调用此函数。这个函数叫做运算符重载函数,通常为类的成员函数。运算符重载函数定义:返回值类型类名:operator重载的运算符(参数表)operator是关键字,它与重载的运算符一起构成函数名。因函数名的特殊性,C+编译器可以将这类函数识别出来。,细解运算符重载:复数类+的重载:ComplexComplex:operator+(Complexc)ComplexTemp(Real+c.Real,Image+c.Image);returnTemp;编译器把表达式c=c2+c3解释为:c2.operator+(c3);函数c2.operator创建一个局部的Complex对象Temp,把出现在表达式中的两个Complex类对象c2和c3的实部之和及虚部之和暂存其内,然后把这个局部对象返回,赋给Complex类对象c。,4.5运算符的重载,Temp.Real=Real+c3.Real;Temp.Image=Image+c3.Image;c=return(Temp);,RealImage,c3.Realc3.Image,=,+,局部对象Temp,当前对象c2,对象c3,隐式返回计算结果:省略局部的Complex对象TempComplexComplex:operator+(doubled)returnComplex(Real+d,Image);/隐式说明局部对象在return后面跟的表达式中调用的是类的构造函数,它为无名对象赋值(初始化),返回值就是该无名对象。,4.5运算符的重载,说明:ComplexComplex:operator+(Complexc)ComplexTemp(Real+c.Real,Image+c.Image);returnTemp;当成员函数的参数为同一类(class)的对象或它的引用,在函数体内使用参数对象的私有数据成员时,可用对象名加成员访问操作符点号进行。从逻辑上讲,每个对象有自己的成员函数,访问同类其他对象的私有数据成员应通过该对象的公有函数,不能直接访问。但在物理上只有一个成员函数代码,所以直接访问是合理的。仅在成员函数中可以这样做。,【例4.8】复数类,应用它进行复数运算,【例4.8】复数类,classComplexdoubleReal,Image;public:Complex(doubler=0.0,doublei=0.0):Real(r),Image(i)Complex(Complex,【例4.8】复数类,ComplexComplex:operator+(doubled)/重载+returnComplex(Real+d,Image);ComplexComplex:operator+=(Complexc)/重载+=Complextemp;/为了返回Complex类型的值,使+=可以连续使用temp.Real=Real+c.Real;temp.Image=Image+c.Image;Real=temp.Real;Image=temp.Image;returntemp;ComplexComplex:operator=(Complexc)/重载=Complextemp;/定义temp为可返回Complex类型值,使=可连续使用temp.Real=c.Real;temp.Image=c.Image;Real=temp.Real;Image=temp.Image;returntemp;doubleComplex:abs(void)/求绝对值函数returnsqrt(Real*Real+Image*Image);ComplexComplex:operator*(Complexc)/重载*returnComplex(Real*c.Real-Image*c.Image,Real*c.Image+c.Real*Image);,【例4.8】复数类,ComplexComplex:operator/(Complexc)/重载/doubled=c.Real*c.Real+c.Image*c.Image;returnComplex(Real*c.Real+Image*c.Image)/d,(Image*c.Real-Real*c.Image)/d);intmain(void)Complexc1(1.0,1.0),c2(2.0,2.0),c3(4.0,4.0),c;doubled=0.5;c1.Print();c=c2+c3;c.Print();c+=c1;c.Print();c=c+d;c.Print();/可用0.5代替dc=c3*c2;c.Print();c=c3/c1;c.Print();coutc3的模为:c3.abs()endl;c=c3=c2=c1;c.Print();/连续赋值c+=c3+=c2+=c1;c.Print();/连续加赋值return0;,4.5运算符的重载,引用作为参数:ComplexComplex:operator+(constComplex注意:参数采用对象的引用而不是对象本身,调用时不再重新分配内存建立一个复制的对象,函数效率会更高。而在引用形式参数类型说明前加const关键字,表示被引用的实参是不可改变的,如程序员不当心在函数体中重新赋值了被引用的实参,C+编译器会认为出错。,const引用进一步说明:引用在内部存放的是被引用对象的地址,不可寻址的值是不能引用的;当引用作为形参时,实参也不能使用不可寻址的值,更不可能进行类型转换(如:实数转换为整数)。但是const引用不同,它是只读的,为了绝对保证不会发生误改,编译器实现const引用时,生成一个临时对象,引用实际上指向该临时对象,但用户不能访问它。所以const引用可以实现不可寻址的值(包括字面常量)的引用。,例如:doubledval=1024;constint因有临时对象,引用和类型转换都实现了。当const引用作为形参时,实参也能使用不可寻址的值,并能进行类型转换。,4.5运算符的重载,默认的复数复制赋值操作符:函数声明:Complex,重载的运算符“+=”标准算法声明是:Complex,2.当用类的成员函数实现运算符的重载时,运算符重载函数的参数(当为双目运算符时)为一个或(当为单目运算符时)没有。运算符的左操作数一定是对象,因为重载的运算符是该对象的成员函数,而右操作数是该函数的参数。,3.单目运算符“+”和“-”存在前置与后置问题。前置“+”格式为:返回类型类名:operator+()而后置“+”格式为:返回类型类名:operator+(int)后置“+”中的参数int仅用作区分。,小结:1.运算符重载函数的函数名必须为关键字Operator加一个合法的运算符。在调用该函数时,将右操作数作为函数的实参。,4.C+中只有极少数的运算符不允许重载。,4.5运算符的重载,问题:例4.8中:c=c+d;改为c=d+c;因为d不是Complex的对象,C+编译器将无法找到合适的重载的“+”运算符对应的函数,最终给出出错信息。,怎样解决?,4.6友元,在C+中友元(friend)函数允许在类外访问该类中的任何成员,就象成员函数一样。友元函数用关键字friend说明。,上节答案:用友元函数重载运算符“+”,可以实现c=d+c;,4.6友元,classComplexfriendComplexoperator+(double,Complex);/opration+说明为类Complex类的友元函数,/friend只用于类说明中,定义时不加friendComplexoperator+(doubled,Complexc)returnComplex(d+c.Real,c.Image);/注意友元不是成员函数,但以直接访问私有成员intmain(void)c=d+c1;c.print();return0;解释:d+c被C+编译器解释为:operator+(d,c),友元函数重载运算符形式:+有三种形式。另两个的声明为:friendComplexoperator+(Complex,Complex);friendComplexoperator+(Complex,double);涵盖实数与复数,复数与复数,复数与实数相加三种情况。,可以仅使用友元函数friendcomplexoperator+(complex,complex);实数被默认的构造函数强制转换为虚部为零的复数。d+c1被解释为:operator+(complex(d),c1)注意:传值,在函数内是建立了两个复数对象,而把实参的值传进去,进行运算。,比较:友元函数可以有两个参数,而对应的成员函数只有一个参数,所以友元函数的使用可以更灵活、更方便。,图4.9友元函数operator+执行过程内存分配,改进:Operator+友元函数的声明可使用引用类型变量friendComplexoperator+(constComplexpublic:Complex(doubler=0.0,doublei=0.0):Real(r),Image(i)Complex(Complex,【例4.8_1】用友元函数重载运算符,Complex,【例4.8_1】用友元函数重载运算符,intmain(void)Complexc1(1.0,1.0),c2(2.0,2.0),c3(4.0,4.0),c;doubled=0.5;c1.Print();c=c2+c3;c.Print();/两复数相加c+=c2+=c1;c.Print();/连续加赋值c=c+d;c.Print();/复数加实数c=d+c;c.Print();/实数加复数c=c3*c2;c.Print();c=c3/c1;c.Print();c=c3*d;c.Print();/复数乘以实数c=c3/d;c.Print();/复数除以实数coutc3的模为:abs(c3)endl;return0;,4.6友元,单目运算符前“+”的成员函数重载方式如下:ComplexComplex:operator+()returnComplex(+Real,+Image);,友元函数重载后置“+”如下:friendComplexoperator+(Complex采用引用类型,后“+”是直接施加于实参。否则施加于副本,而实参不变。,注意:复制赋值运算符(=)重载必须为成员函数,不可为友元函数。因为默认的复制赋值运算符(=)是成员函数,友元函数不能取代它。,友元函数注意点:1.友元函数不是类的成员函数,在函数体中访问对象的成员,必须用对象名加运算符“.”加对象成员名。但友元函数可以访问类中的所有成员,一般函数只能访问类中的共有成员。2.友元函数不受类中的访问权限关键字限制,可以把它放在类的公有、私有、保护部分,但结果一样。3.某类的友元函数的作用域并非该类作用域。如果该友元函数是另一类的成员函数,则其作用域为另一类的作用域,否则与一般函数相同。,友元类:整个类可以是另一个类的友元。友元类的每个成员函数都是另一个类的友元函数,都可访问另一个类中的保护或私有数据成员。定义方法如下:classAfriendclassB;/声明B为A的友元类;,4.7静态成员,由关键字static修饰说明的类成员,成为静态类成员(staticclassmember)。但与函数中的静态变量有明显差异。类的静态成员为其所有对象共享,不管有多少对象,静态成员只有一份存于公用内存中。,4.7.1静态数据,4.7.2静态函数成员(选读),4.7.1静态数据,静态数据成员定义与使用:在类定义中,用关键字static修饰的数据成员为静态数据成员。该类所有对象共享由系统为静态成员分配的一个存储空间,而这个存储空间是在编译时分配的,在定义对象时不再为静态成员分配空间。静态数据是该类所有对象所共有的,可提供同一类的所有对象之间信息交换的捷径。静态数据成员属于整个类,使用时可用以下格式:类名:静态数据成员名,#includeclassCtestprivate:staticintcount;/注意私有public:Ctest()+count;cout对象数量=countn;Ctest()-count;cout对象数量=countn;intCtest:count=0;/A行对静态数据定义性说明intmain(void)Ctesta3;return0;,【例4.9】用静态数据成员计算由同一类建立的对象的数量,执行程序后输出:对象数量=1/a0构造函数产生对象数量=2/a1构造函数产生对象数量=3/a2构造函数产生对象数量=2/a2析构函数产生对象数量=1/a1析构函数产生对象数量=0/a0析构函数产生,4.7.2静态函数成员(选读),静态函数成员的使用:函数成员说明为静态,将与该类的不同对象无关。静态函数成员的调用,在对象之外可以采用下面的方式:类名:函数名(对象名,参数表);任一类对象名:函数名(对象名,参数表);静态函数成员多为公有的。在例4.8中的复数类中的函数成员print(),改为静态的则可如下表达:staticvoidprint(complex参数是为了告诉C+系统应取哪一个对象的数据。,4.8结构,结构类型的引入:在C+中结构(structure)与类几乎是完全一样的类型,差别仅仅在于默认情况下结构的成员为公有的。在C语言阶段,结构就已存在,但它只有公有的数据成员。正因为如此,C+程序员仍然使用结构,但是只为结构安排公有的数据成员。因为这样程序更易读易懂。在程序设计中常把结构类型的数据作为类的数据成员。,C风格的定义:struct结构类型名类型名变量1;类型名变量2;;/最后的分号不可少,4.8结构,实例:structinventory/库存货物chardescription15;/货物名称charno10;/货号intquantity;/库存数量doublecost;/成本doubleretail;/零售价格structemployee/员工charname27;/员工姓名charaddress30;/家庭住址longintzip;/邮政编码longinttelenum;/联络电话doublesalary;;/工资,4.8结构,变量定义与初始化:结构是一种派生数据类型,定义结构时并不分配存储空间,只有定义了结构类型的变量,编译系统才为结构变量分配存储空间。定义变量方法如下:inventorycar,motor;初始化是用花括号中顺序填入结构中的(公有数据)成员的初始值完成的:employeeemp1=“朱明”,“四牌楼2号”,210096,3792666,2430.0,

温馨提示

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

评论

0/150

提交评论