第5章类和对象_第1页
第5章类和对象_第2页
第5章类和对象_第3页
第5章类和对象_第4页
第5章类和对象_第5页
已阅读5页,还剩54页未读 继续免费阅读

下载本文档

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

文档简介

第5章类和对象本章提要面向对象编程的引入;描述抽象、封装和类;描述C++程序的典型结构;访问限定符;静态变量和静态函数;友元函数和友元类;构造函数和析构函数引子软件有着极高的复杂性。想想我们的案例,“航空货物托运费用计算程序”由初始化变量、输入数据、计算费用、输出结果等部分组成,但必须告诉大家,这还不算复杂,还不及真正的系统的千分之一。案例是我们经过了删除、简化、修改才得出来的描述,真正的“航空售票、货物托运系统”有着惊人的复杂性,并不是一二十人在一两个月就可以完成的事情。再比如,银行系统、电信收费系统、航天控制系统等等,这些软件所涉及的人、物、机器等是如此之多,过程也极其复杂。只要其中一个环节出了问题,那么就会带来上千万、甚至几十亿元的损失。软件系统复杂性的出现是由于软件内部和外部的因素造成的。第一,内部的复杂性来自于系统运行模式的复杂性。第二,外部的复杂性来自于用户与软件设计者之间的沟通。“软件的复杂性是一个本质的性质,不是一种偶然的性质。”——Brooks5.1世界是由对象构成的有机体现实世界的复杂性软件系统的复杂性是不可能摆脱的,怎么样来简化软件系统的复杂性呢?利用面向对象方法,先把系统分解为若干个组成对象,它们彼此交互,最后用这些对象“装配出”整个系统软件。打个比方,一个大集团的运作是相当复杂的,但是如果将它分解为各个部门,各个层次,它们之间互相联系,各自负责相应的工作,那么整个集团的运作就会变得一目了然了。世界是由对象构成的有机体在物理实体中,我们把对象定义为:“可以看得见,摸得着的实体。”同理,“人为的概念,或者是任何有明确边界与意义的东西,也是对象”。它们都有着同一的特征:拥有状态和行为。状态是对象的一个或多个属性的描述。行为是对象为了改变自身的状态而发生的作用和反作用。简单来说,在软件中,对象就是一组变量和相关方法的集合,其中变量表明对象的状态,方法表明对象所具有的行为,即我们所学过的函数。当然,我们可以将现实世界中的对象经过抽象,映射为软件中的对象。对象在软件中是通过一种抽象数据类型来描述的,这种抽象数据类型称为类(Class);用更严谨的话来表述,“对象是封装了数据结构及可以施加在这些数据结构中的操作的封装体,这个封装体有可以唯一地标识它的名字,而且向外界提供一组服务(即操作)。”概括地说,面相对象方法具有下述四个要点:①

认为客观世界是由各种对象组成的,任何事物都是对象,复杂的对象可以由比较简单的对象以某种方式组合而成;②

把所有对象都划分成各种对象类(简称为类,Class),每个对象类都定义了一组数据和一组方法;③

按照子类与父类的关系,把若干个对象类组成一个层次结构的系统;④对象彼此之间仅能通过传递消息互相联系;5.2抽象、封装与类什么是抽象、封装由于不能应付对象的复杂性,人们就选择了这样的方法:抛弃非本质的细节,集中于我们最关心的材料。“抽象是指区别于所有其它种类对象的‘对象的本质特征’,因此,相对于观察者来看,它提供了清晰的、有定义的概念边界。”

用相关的方法把变量包围起来与外界隔离,称为封装。封装后的对象外界不能直接访问其内部数据,各对象间必须通过消息来通信。换一句话来说,封装实际上就是使用方法将类的数据隐藏起来,控制用户对类的修改和访问数据的程度。什么是类“类”就是对具有相同数据和相同操作的一组相似对象的定义,也就是说,类是对具有相同特性和行为的一个或多个对象的描述。有了类这个概念之后,我们就可以实现面向对象编程4个关键的组成部分:抽象、继承、封装和多态了。举例:

classCar软件中的类

{intcolor;intnumber;变量

intspeed;

voidbrake(){…}voidspeedUp(){…}方法

voidslowDown(){…}}什么是对象与类对应的就是实例,实例就是由某个特定的类所描述的一个具体的对象。打个比方,我们所说的“人类”,它是一个概念,是对人的一个定义,人类里有一个具体的人,他的名字叫“张三”,“张三”就是一个具体的对象了,他是实实在在的存在着的。那么,“人类”就好比类,“张三”就好比实例了。“对象是类的实例,类是有公共特性的对象的抽象。”比如:

GarbageTruckg;g.brake();g.LoadGarbage();一个类可以定义出无数个对象,各个对象之间相互独立,互不影响。用C++描述、识别类用C++识别类:① 用英语写出需求的陈述;② 用把名词标识出来;(名词代表了一个实体)选择有用的名词作为类名;决定要将哪些有用的名词作为类。5.3面向对象的C++程序的典型结构第一个面向对象的C++程序一个典型的C++程序一般由以下几部分组成:头文件、类(类中包含数据成员、成员函数)和main()函数。例:#include<iostream.h>//头文件classCar//类{public://访问限定符

voidback()//成员函数

{ cout<<"Danger,backingacar!";}};intmain()//主函数{Carc1;//创建对象

c1.back();return0;}(1)头文件文件包含命令:#include<头文件>,放在C++程序的开头。(2)类类是对具有相同特性和行为的一个或多个对象的描述。①类的定义类定义包含两部分:类头(classhead)、类体(classbody),类定义后面必须接一个分号。②数据成员类数据成员的声明方式跟变量声明相同,如:③成员函数C++中,对象的函数被称为成员函数,如:④创建对象类是对具有相同特性和行为的一个或多个对象的描述,类的定义(如类Car)不会引起内存分配,只有当创建了一个类的对象时,系统才会分配内存。对象是类的实例,是具体存在着的。例如:classCar{

…};intmain(){Carc1,c2;//创建了c1,c2两个对象}只有创建了类Car的对象时,才分配一块足够包含Car类的数据成员的存储区,名字c1引用到这块存储区。每个类对象都有自己的类数据成员的拷贝,修改c1的数据成员不会改变任何其他Car对象的数据成员,例如不会修改c2。创建了类的对象之后,就可以用成员访问操作符来访问类对象的数据成员或成员函数了,成员访问操作符——点“

.”或箭头“

->”,例如:Carc1;c1.number=888;c1.back();(3)main()函数对于每个C++程序,都必须定义一个称作main()的函数,它是C++程序开始执行时第一个调用的函数。当main()函数执行完毕(也就是整个程序执行完毕)时,它需要通知操作系统它已经执行完毕了。return0;意味着程序执行完毕且没有错误。如果返回给操作系统的是任何非0的数,这意味着程序执行时出现了某种错误。作用域分解运算符为了能够很快地了解一个类中具体包括哪些属性和方法,使我们编写的C++程序有更好的可读性,C++允许将类中的方法的具体实现放置在类定义的外面;为了做到这一点,必须使用C++中提供的作用域分解运算符“::”来定义这些方法。classCargo{public:floatweight;//成员函数的原型说明,决不可以省略;

voidinit();};//利用作用域分解符将类的成员函数放在类定义之外;voidCargo::init(){ weight=0.0;}关于类的this指针当类的函数的形参与这个类的数据成员同名时,在C++中,用this关键字指向于当前正在执行的类对象,采用this关键字来告诉C++编译器到底要访问哪个变量。例如:classCargo{public:floatweight;//成员函数的原型说明,决不可以省略;

voidinit();};voidCargo::init(floatweight,floatcharge){ this->weight=weight; this->charge=charge;}单元练习:定义一个类Video,它表示录像带租赁店中的录像带。试考虑在录像带租赁管理程序中,为使录像带类运行良好,必须设计的属性和方法,请包含一个能显示录像带信息的print()成员函数。5.4访问限定符public访问限定符公有成员(publicmember)在程序的任何地方都可以被访问。举例如下:classSerApp{public: charname[30];//公有成员};intmain(){SerAppc;cin>>;//访问namereturn0;}private访问限定符私有成员(privatemember)只能被该类的成员函数和类的友元访问(友元将在后面讲述),不能被其他类对象或函数访问(子类也不行)。这样就实现了信息隐藏,信息隐藏可以使数据不受外部变动的影响。举例如下:classSerApp{private://私有成员

floatimprest;voidwarning() { cout<<"BEEP!Ifyouisnotthepeopleonmyownside,youcanseethisnew!"; }public://公有成员

voidaccept() {cin>>imprest; }};intmain(){SerAppc;c.accept();}错误代码:cin>>c.imprest;或c.warning();//提示出错:error:cannotaccessprivatememberprotected访问限定符被保护成员(protectedmember)对派生类(子类)就像public成员一样,对其他程序则表现得像private成员一样。protected访问限定符有意义的用法是在实现继承时用到。三种访问限定符的类成员的可见性:单元练习:创建一个类,具有public、private和protected数据成员和成员函数。在main()函数中,创建该类的一个对象,看看当试图存取所有的类成员时会得到一些什么编译信息。5.5静态变量和静态函数静态变量在类体中的变量声明前面加上关键字static,就使该变量成为静态的。对于非静态变量,每个类对象都有自己的拷贝;而静态变量对每个类只有一个拷贝,静态变量可被该类的所有对象共享访问。还有,静态变量即使在它所属于的函数执行完后还是保留他们的值,这意味着:静态变量在整个程序内一直保留着它的值。静态变量必须在其类体内声明,但不能在类体内初始化,只能在成员函数或类定义之外(类体外)被初始化。静态变量的声明和初始化,举例:classSExample{public:staticintsvar;//静态变量声明

…}intSExample::svar=0;//静态变量初始化①在类的成员函数中可以直接访问该类的静态变量而不必使用成员访问操作符:SExample::cout(){svar=svar+1;cout<<”Now,Thetotalis:”<<svar;}②但是,在非成员函数中我们必须以以下两种方式之一访问静态变量:a.可以使用成员访问操作符:intSExample::svar=0;intmain(){SExamples1;s1.svar=1;}b.因为类静态变量只有一个拷贝,所以不一定要通过对象或指针来访问,访问静态变量的另一种方法是用被类名限定修饰的名字直接访问它:intSExample::svar=0;intmain(){SExample::svar=1;}

当不通过类的成员访问操作符访问静态变量时,必须指定类名以及紧跟其后的域操作符“

::”。静态函数静态函数的声明就是在类体中的函数声明前加上关键字static。因为静态成员在对象创建之前就已经存在了,所以,静态函数只能访问静态变量,不能够访问非静态变量。静态函数声明举例:classSExample{public: staticintsvar;staticvoiddisplay()//静态函数声明

{ cout<<"Thestaticvariableis:"<<svar; }};intSExample::svar=0;与静态变量同理,也有两种方式访问静态函数。a.可以用成员访问操作符——点“

.”

和箭头“

->”,为一个类对象或指向类对象的指针调用静态函数。b.也可以用类限定修饰名直接访问或调用静态函数,而无需声明类对象。举例:

SExample::display();//访问方式a SExamples; s.display();//访问方式b案例提出问题:改善我们的“航空货物托运费用计算程序”,航空公司能根据旅客的数量随意增加输入的信息,并能随时显示当前的旅客数量。而且,分段收费要留有接口,以便以后增加更多、更细的收费标准。分析问题:使用静态变量存储当前旅客数量使用静态变量存储分段收费标准使用静态函数显示当前的旅客数量编写代码编译程序检验程序执行解决问题:见书本单元练习:

创建一个类Monitor,它能知道它的成员函数incident()被调用了多少次,增加一个成员函数print()显示incident()被调用的次数。5.6友元函数和友元类友元函数在某些情况下,允许某个特殊函数而不是整个程序可以访问类的私有成员,这样做会比较方便。于是,友元(friend)机制允许一个类授权其他的函数访问它的非公有成员。友元函数的声明以关键字friend开头,它只能出现在类的声明中,它不受类体中声明的public、private和protected区的影响。任何非成员函数都可以由类声明为friend(友元函数),那么,该函数就可以直接访问此类对象的非public成员了。例如:classFExample{friendvoidcall1();//说明“call1()函数是朋友,它可以用x了”;

private:intx;}voidcall1(){FExamplef;f.x=0;//运行通过,因为声明了友元函数call1();}voidcall2(){FExamplef;f.x=0;//错误,因为call2()函数并没有被声明为友元函数;}友元类在使一个类成为友元时,友元类的所有成员函数都被给予了访问“授权友谊的类的非公有成员”的权力。举例如下:classFExample{friendclassFClass1;//说明“FClass1类是朋友,它可以用x了”;

private:intx;};classFClass1{public://“FClass1类是FExample类的朋友”,

voidcall()//FClass1类的所有成员函数都可以访问FExample类的非公有成员;

{ FExamplef; cin>>f.x;//声明了友元类,可以访问私有成员x }};单元练习:创建三个类,第一个类包含private数据,并且第二个类和第三个类所有的成员函数都是它的友元,在main()函数中演示一下它们是如何正确运行的。5.7构造函数及析构函数

构造函数(1)为什么需要构造函数每当我们要创建对象时,都要为它分配内存,但是,分配内存并不是数据成员的初始化。当然,针对这个问题,可以写一个初始化的函数,在函数中对每个数据成员赋初值,然后,每当创建一个对象时,就调用该函数一次。但是,这样来初始化很不方便,如果忘记了调用这个初始化函数,那么运算就会出错。在C++中,初始化实在太重要了,类的设计者为我们提供了更好的解决方案。当声明一个类时,在类里包含一个特殊的成员函数,每当创建类的对象的时候,计算机就自动调用此函数。这种函数称为构造函数。(2)声明构造函数构造函数是一个可以被重载的用户定义的成员函数,它在创建类的对象的时候自动被调用。构造函数的特点:它的函数名与类名相同,它不能指定任何返回类型,甚至void也不行。举例如下:classCargo{public: Cargo();};Cargo::Cargo()//构造函数;{cout<<"\nConstructorhasbeeninvoked!";}(3)带参数的构造函数一个类可以有多个构造函数,只要各个构造函数的参数的类型不同、或者个数不同、或者是排列顺序不同就可以了,计算机会根据参数的不同而自动调用相应的构造函数。定义带参数的构造函数,举例如下:classCargo{public: Cargo(floatinitCharge);};Cargo::Cargo(floatinitCharge)//带参数的构造函数;{ charge=initCharge;//给变量discount赋初值;}intmain(){ Cargoc(5);//在创建对象时,必须带上构造数的实参; return0;}析构函数(1)为什么需要析构函数另一种情况是,我们还缺少一种对称的操作,它为生命期即将结束的类对象再次初始化,返还相关的资源,或者说自动释放已获得的资源。析构函数就是这样一个特殊的类成员函数,它是构造函数的互补。当该类的对象离开了它的作用域,或者该对象的指针被delete时,析构函数会自动被调用。(2)声明析构函数构造函数的特点:析构函数的名字是在类名前加上波浪线(“

~”),它不返回任何值,也没有任何参数。因为它不能指定任何参数,所以它也不能被重载。尽管可以为一个类定义多个构造函数,但是,只能提供一个析构函数,它将被应用在该类的所有对象上。举例说明如下,:classCargo{public: Cargo();~Cargo();};Cargo::Cargo(){owner=newchar[15];//动态分配内存;}Cargo::~Cargo()//析构函数;{cout<<"\nDestructorhasinvoked!"<<endl; delete[]owner;//释放内存;}注意

①构造函数不能显式地被调用,例如:c.Cargo();//错误但是,析构函数却不同,它可以显式的多次被调用,如果有必要的话。语法跟成员函数的调用一样,例如:c.~Cargo();②当使用new运算符动态创建对象的指针时,new运算符也会自动调用构造函数的;当使用delete运算符释放指针时,那么,delete运算符也会自动调用析构函数。对象的生命周期考察以下程序,用来说明类Lifecycle对象的生命周期:#include<iostream.h>classLifecycle{staticinti;intflag;//变量flag用来标明是第几个对象

public: Lifecycle()//构造函数

{ i++; flag=i;cout<<"\nObj["<<flag<<"]'sConstructorhasbeeninvoked!"; } ~Lifecycle()//析构函数

{cout<<"\nObj["<<flag<<"]'sDestructorhasbeeninvoked!"; }};intLifecycle::i=0;Lifecycleobj1;//创建第一个类对象intmain(){cout<<"\nProgramstartinhere!"; Lifecycleobj2;//创建第二个类对象

{ cout<<"\nTheblockstartinhere!"; Lifecycleobj3;//创建第三个类对象

cout<<"\nTheblockfinishinhere!"; } cout<<"\nProgramfinishinhere!"; return0;cout<<"\nHere,youcan'tseeanything!";}运行上面的程序,我们看到的结果将会是:Obj[1]'sConstructorhasbeeninvoked!Programstartinhere!Obj[2]'sConstructorhasbeeninvoked!Theblockstartinhere!Obj[3]'sConstructorhasbeeninvoked

温馨提示

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

评论

0/150

提交评论