面向对象程序设计_第1页
面向对象程序设计_第2页
面向对象程序设计_第3页
面向对象程序设计_第4页
面向对象程序设计_第5页
已阅读5页,还剩45页未读 继续免费阅读

下载本文档

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

文档简介

1、第1章 概述面向对象程序设计,是一种以面向对象的思想和方法、以一种编程语言进行程序设计的过程。在该过程中包含两个方面的含义:其一,什么是面向对象的思想和方法,有什么优势,有什么作用;其二,在程序设计语言中是如何体现面向对象的思想和方法的,在使用过程中应该具备哪些基础,又该注意哪些问题。本章首先从面向对象的基本概念出发,介绍面向对象的思想与机制、面向对象程序设计的优势、目前常用的程序设计语言和工具等内容。由于在本书中需要用到大量的实例,其中会涉及到具体语言中的某些特性,所以在本章最后一部分给出相关的语言基础。1.1 面向对象思想与机制面向对象(Object-oriented,简称OO)是一种思维

2、方法,其核心思想即为归纳和演绎的方法。实际上,人们认识世界和发展世界的过程就是面向对象的。本节首先对人们认识世界和发展世界的过程进行分析,进而引出面向对象的基本概念和相关机制。1.1.1 人类认识世界和发展世界的过程当一个新生儿从呱呱坠地后,便开始学习。学习是过程是从认识一个又一个具体的“实物”开始的,这些实物包括动物(小猫小狗小鱼小鸟乃至小虫子)、植物(花草)和人(爸爸妈妈叔叔阿姨爷爷奶奶)。先从这些单一实物中得到其个体特征,并逐步对这些个体特征进行综合得到共同特征。从上幼儿园到小学、中学、大学,不断地上课,不断地接触学科和老师,在一门课中通过一个又一个的“例子”和“练习”得到提升。这些过程

3、都是归纳的过程,是一种从有形到无形的过程,也是一种从实例到知识的过程。其总体表现形式为:个体个性共性。参加工作之后,需要将自己所学到的知识服务于社会,为社会发展作出应有的贡献。在这个阶段,人们都需要将个体能力充分地发挥出来。而发挥个人能力的方式有很多种,但归纳起来无外乎两种方法:其一,通过知识融合包括自身知识的融合以及借鉴前人的经验;其二,在自身已有知识或者他人已有经验的基础上发挥“想象”,添加一些“前所未有”的“创造”。这两种方法正好是归纳过程的逆过程,也就是演绎的过程,是一种从无形知识到有形产品的过程。其具体表现形式为:共性个性个体。在面向对象思想中,归纳被称为“抽象”,演绎被称为“派生”

4、。抽象和派生,构成面向对象的“继承”机制。1.1.2 面向对象的基本概念 对象从1.1.1我们得知,归纳的过程需要以大量的“实例”为基础。这些实例称为“对象”。对象在现实世界中也称为“实体”,在知识层面也称为“实例”。为了更确切地理解对象的含义,可将其分为两大类:物理对象(物理实体):在现实生活中看得见摸得着具有一定实际尺寸的实际存在的“物体”。比如:张三(人),太阳,这只花猫,那棵白菜等。逻辑对象(逻辑实体):这类对象也是真实存在的,但不一定看得见摸得着。比如课堂上的“例子”,法官断案时的“证据”,讲道理时所需要摆的“事实”等。 类世间万物,形形色色。人们要认识世

5、界,不可能需要认识世界上的每一个“物”,只需要认识有代表性的少数就够了。通过举一反三的思想,便可以知道相似的“物”是什么。物,是实际存在的。而知道“物”是什么,则是知识,构成“类”。因此,“类”是相似物体的高度抽象,或者说,类是相似对象的特征抽象。在现实生活中,几乎所有的名词都是类,如:桌子、星球、人、动物。人们知道电脑,但未必一定要看得见一台电脑。那么,在知识层面上,一个类是个什么样子呢?简单来说,一个类都包含有若干“成员”,这些成员代表了同类对象的“特征”:人,有姓名、年龄、身高、体重,也有思想,有动作,遇到突发事件时有回应。树,有高度、宽度、命名,适度浇水可以生长,扒掉树皮就会死亡。电脑

6、,有品牌、显示器、主板、CPU,加电就会运行,点击就有反应。从这几个例子可以看出,每个类的特征数量虽然有多有少,但都可以分为静态部分和动态部分。前者称为物理特征,或属性,或数据,后者称为行为特征,或动作,或方法。归结起来,一个类的由数据成员和方法成员构成的。 类与对象一个对象是个体存在,一个类是相同对象的共性。一个类只具有特征,但每一个特征都没有具体值。一个对象不仅具有特征,而且每一个特征都可以赋予具体值。一个简单名词构成类。而在该名字前加上“这个”、“那个”等限定词后则构成对象。人们认识世界的过程,是从对象到类的过程。人们发展世界的过程,是从类到对象的过程。 类的

7、组织类是群体对象的抽象,不同类的对象将抽象出不同的类。现实生活中的类很多,类和类之间的有机组织构成认识世界的基础。类的组织结构有两种:泛化特化结构:类和类之间存在特征包含关系。整体部分结构:一个类的成员是另一个类的对象。 消息传递现实世界由对象构成。由于存在的对象太多,所以要进行归类。类只是一种抽象,表征了一种结构,是虚的,是一种意识形态,是一种认识同类对象的方法。而世界是实际存在的。要使世界运转,生活持续,必须构建出各种各样实实在在的对象,并且使用这些对象。对象和对象之间是有关系的,彼此在“通信”和“交互”。这种交互过程就称为对象之间的消息传递。对象之间的消息传递是依靠“消息请

8、求”、“消息处理”和“消息应答”过程来完成的。其中的“消息请求”和“消息应答”存在与两个对象之间,“消息处理”则是对象内部的动作或活动。消息传递机制可以有效地屏蔽消息处理的具体过程和内部细节,只对外呈现出相应的“接口”。正如,人对外提供的接口包括“口”、“耳”、“眼”等,但“消息处理”的过程大脑活动的细节和思维方法则是对外隐藏的。1.1.3 面向对象机制面向对象机制包括封装、继承和多态。 封装面向对象的封装机制是其中最简单的一种机制,包含两个层面的含义:一个对象,既有静止特征的成分,又有活动特征的成分。将静态成分抽象为数据,活动成分抽象为方法,一个对象就是由其数据和方法进行“封装

9、”后构成的“单元”。而一个类又是对象特征的抽象,所以类的构成和对象的构成是一致的。这就是封装机制的第一层含义。同时,作为一个对象而言,其特征部分有对外公开的,也有私密的(对外不可见)。这种能够实现特征信息隐藏的能力构成面向对象封装机制的第二层含义。在本书第2章将给出与封装机制相关的详细内容。 继承继承是面向对象三大机制中功能最为强大,使用也最为复杂多变的机制。简单来说,所谓继承,是指用现有的类生成新类的机制。现有的类可以是一个,也可以是多个。生成的方式可以是派生,也可以是组合。与继承相关的概念包括:公有继承、私有继承和受保护继承;父类、直接父类、间接父类、子类;基类、派生类;部分

10、类、整体类;单继承、多继承、虚继承。在本书Error! Reference source not found.中给出与继承机制相关的详细内容。 多态作为面向对象的第三个机制,多态同样占据着非常重要的地位。从现实生活来讲,多态可以通过以下两个层面进行理解:一,相同的消息发送给不同的对象,产生不同的结果。比如,在课堂上老师要求每个学生独立画一幅画。相同的消息是“请画一幅画”,不同的对象是每个学生,结果肯定是多种多样的。二,相同的消息发送给不同的对象,尽管产生的结果相同,但每个对象的内部实现原理不同。比如,同样的刹车动作(踩刹车),无论是小汽车还是大货车都可以达到减速甚至停车的目的,但

11、小汽车的刹车原理是“油刹”,而大货车则是“气刹”。对于面向对象程序设计中的多态应用,不同的语言采用的方法不同。在本书Error! Reference source not found.将给出C+语言中实现多态的方法和相关问题。1.2 面向对象设计与结构化设计1.2.1 基本编程方法比较结构化程序设计的主体元素包括模块、函数和过程。在C语言中,一个程序是有一个主函数和若干个非主函数所构成。每一个函数都包括了函数头和函数体,函数体中又可以定义局部变量和代码片段。而当人们采用结构化思想编写程序时,对于需要在多个地方出现的相同或者相似代码,就可以进行抽象以构成函数,以参数的形式代替有区别的地方,从而实

12、现模块化结构。比如,有下面的需求:张三的语文书的第38页的第1个字符是什么?李四的数学书的第50页的第8个字符是什么?王五的外语书的第23页的第20个字符是什么?经过分析后发现,这些需求的大体描述都是一样的,区别的地方有4个,分别是谁的书,什么书,第多少页,第多少个字符。经过抽象,可以构造一个函数如【例1-1】所示。【例1-1】 结构化抽象的例子char getx(char *who,char *book,int page,int pos)/函数具体操作void main()cout<<getx("张三","语文",38,1);cout<

13、;<getx("李四","数学",50,8);cout<<getx("王五","外语",23,20);这种抽象是直接的,也是掌握了结构化程序设计方法之后最容易想到的方法。但这种方法最大的问题在于后期的更新和维护。一旦需求发生变化,或者环境发生了变化,getx函数就需要进行较大规模的调整,甚至是完全废弃。比如:不同人的同一名称的书是否是一样的。同一种书的相同页面是否是一样的。而如果每一个人每一本书上都不相同,那么采用这种函数抽象的意义就不复存在。而面向对象程序设计方法主张以对象为单位。需求中的人(包

14、括张三、李四和王五)都是对象,不同的书(数学、语文、外语)也都是对象,每个对象彼此独立存在,互不干扰,从而构成下面的例子:【例1-2】 面向对象抽象的例子void main()cout << 张三.语文.C381;cout << 李四.数学.C508;cout << 王五.外语.C2320;显然,【例1-2】还需要给出类和对象的定义,此处只是一个不完整的例子,只是说明面向对象编程的一个基本思想和方法。在该例子中,张三的语文和李四的语文完全可以不同,彼此独立,能够很好地实现个体维护而不影响其他。1.2.2 面向对象方法的优势简单来说,面向对象程序设计方法的优势

15、在于能够很好地适应需求的变化,便于软件的升级。可以考虑这样一个问题:如果现在有一个正在运行的软件需要升级以满足用户的新的需求,应该如何做。结构化方法的解决思路是:打开源代码(如果有的话),分析源代码。将不需要的需求删除,增加或修改源代码以满足新的需求。而如果没有源代码,那么升级就变得不可能。面向对象方法的解决思路是:分析原始文档,对需要变化的类实施派生以产生新类,原有的类不变。而在新类中,对于原来不能满足新需求的成员进行重写,最后适当修改主函数。因此,面向对象程序设计的主要思想是以类为主导的,除了极其简单的主函数之外,其余部分都是类。当需要对程序进行升级时,原有的类都不改变(有时因为类的实现被

16、隐藏而根本无法修改),只需要对有些类进行派生以产生新类(可增加、不可修改也不可删除)。总体来说,面向对象方法的优势体现在以下几个方面:以类为单位组织代码,类间关系采用消息传递(函数调用)。只要类间通信协议不变,类内部的任何修改都不会影响到其他,从而实现代码修改的“封闭性”。当类的内部实现被隐藏之后,功能升级可以通过类的派生机制实现,同样可以满足需求变化。而实际上,目前大量的商用“类库”都是以这种方式提供给用户使用的。作为商用产品,大量成熟的基础“类库”的出现,为人们编写程序提供了极大的方便,诸如通信、多媒体、图形交互界面、数据库访问等。1.2.3 面向对象程序设计的一般过程面向对象的思想是以类

17、为主导的。面向对象程序设计的过程也不例外。一般来说,要构建一个面向对象程序,其过程如下:一,构造类。构造类的过程可以通过系统分析,对需求进行充分理解和关键词提取之后得到。二,构建类和类之间的关系。包括泛化特化关系和整体部分关系。主要采用面向对象的继承机制(包括抽象、派生、组合等)。三,创建应用类对象。类是不能直接使用的。要使用类中成员(数据成员和方法成员),必须创建对象。应用类对象是独立存在的,类似于结构化程序设计中的主函数。四,使用对象。使用对象就是通过使用对象的成员来实现,通常是调用应用类对象中的一个主方法来实现。可以看出,一个真正的面向对象程序是由若干个类所构成的。C+中的主函数不能封装

18、成类,必须是外部函数,但某些编程语言(如C#和java等)是将主运行函数都封装成类之后运行的。但无论如何,主函数应该力求精炼和简短,如下例所示。【例1-3】 一个C+面向对象主函数void main( )CMyApp myapp;/创建一个应用类对象myapp.run( );/调用应用类对象的run方法1.3 常用面向对象程序设计语言和工具作为最经典的面向对象程序设计语言,C+是在C语言的基础上经过功能扩展而构成的。C+完整地包含了C,所以C+也被称之为混合式的面向对象语言。除了C+语言之外,基于.NET环境的C#,java,PowerScript等,都是使用较为广泛的面向对象程序设计语言。就

19、像C+需要VC环境一样,任何一种程序设计语言都离不开工具和环境。语言本身只是一种规范,而要真正达到编写和运行,目前都是采用一种称为IDE(集成开发环境)的工具。通常,不同的IDE适用于不同的开发语言。常用的IDE和开发语言的对应关系如表1-3所示。表1-3 常用面向对象程序设计语言和工具IDE开发语言VC+C+DelphiObject PASCALC+BuilderC+VB扩展BASIC.NETC#、J#等PowerbuilderPowerScriptEclipse、Jbuilder等Java鉴于C语言的普及性,以及C+作为面向对象程序设计语言的经典性,本书采用C+作为编程语言。在本书下面的介

20、绍和例子中,如无特别说明,均以C+作为原型。同时,考虑到例子的简洁性,所有需要用到的头文件均未列出。如果需要以此为例进行实验,请各自增加必要的头文件。1.4 C+基础1.4.1 控制台IO流由于在本书后续的例子中都需要用到输入输出流尤其是输出流,所以在本节开始首先作出相应的介绍。输入输出流包括文件输入输出和控制台输入输出。更详细的介绍参见Error! Reference source not found.。控制台输入输出是最基本的练习方式,通常作为控制台程序中的人机交互的主要成分。回顾C语言中的输入输出,有多种方式,包括getc、getchar、scanf、putc、putchar、print

21、f等,尤其以scanf和printf用得最多。但作为这两个函数调用时的关键部分,格式定义应该与变量类型一致,否则会产生错误的结果。为了进一步简化输入输出函数的调用,C+中使用了IO流,其基本形式如下:输入流:cin>>x>>y>>z;输出流:cout<<x<<y<<z;其中,cin是将键盘输入内容依次分配给后续的变量,cout是将后续变量的值按照标准格式以此从控制台(显示器)上输出。而在输入的过程中,还是应注意输入的值与变量之间的对应关系,包括数字部分和非数字部分、整数部分和小数部分等,不同数值之间也需要用空格分隔。对于输

22、出流cout,虽然从表面上看其输出顺序是从左到右,但处理顺序则是从右到左的。【例1-4】 输出流cout处理顺序和输出顺序char f( )cout << "A" return 'B'char g( )cout << "C" return 'D'void main( )cout << "E" << f ( ) << g( );处理顺序分析:先执行函数g的调用,输出结果C,返回字符D(但没有输出),再执行函数f的调用,输出结果A,返回字符D(依然

23、不输出),最后处理输出部分E(由于是常量,所以没有处理过程)。在处理过程中就已经产生了CA输出。所有的处理过程都结束之后,才得出了主函数cout的各个部分参数,分别为E、B和D,以此输出,形成EBD。合并两部分输出,最后结果为CAEBD。1.4.2 引用引用,是C+中的一种新特征,在提高代码的运行效率方面起着至关重要的作用。 引用变量的定义简单地说,C+中的引用就是别名机制。而所谓别名,也就是在程序代码中多个名字表示着同样的意义,始终具有完全相同的取值。从编译的角度来看,由于在代码中每个对象(变量)都对应着存储空间,所以,引用类型表征着不同名字的对象对应着相同的存储空间。在C+中

24、,引用的简单定义如【例1-5】所示:【例1-5】 引用变量的定义int j;int &k = j;对象j被定义为一个整型,系统将为其分配一个整型大小的存储空间。而紧接着又定义了一个对象k。但此时由于对象k被定义为一个引用(注意引用的定义与C+中指针的定义形式非常接近),而且是已知对象j的一个引用(即别名),因此,在整个程序代码中,对象k和对象j始终表示相同的存储空间,j的改变同时影响到k,反之亦然。如【例1-6】:【例1-6】 使用引用变量int j=0 ;int &k = j;j=10;cout << k << endl; /输出为10,因为j的值发生

25、了变化。endl为回车换行。k+;cout << j << endl; /输出为11,因为k的值发生了变化。endl为回车换行。引用类型常常被用来作为函数的参数,可以有效提高函数调用的运行效率,减少参数传递时由实在参数创建形式参数的过程。同时,引用类型还可以作为函数返回值类型。下面就这两个方面给出相应的说明。 引用类型作为函数参数在函数调用时,通常需要用实在参数替代形式参数。这种替代的过程实际上是用实在参数动态产生一个临时的形式参数空间的过程。实在参数空间和形式参数空间彼此互为独立,但形式参数空间的值需要来自实在参数的值。这无形中就产生了从一个空间到另一个

26、空间的拷贝过程。如【例1-7】所示。【例1-7】 函数调用时的参数传递int f(int a,int b) return a+b; void main( )int x=10,y=20,z;z=f(x,y);当程序运行到对函数f的调用语句时,其具体的执行流程是:首先用实在参数y临时创建一个形式参数b空间并将b空间的值置为y的值,再用实在参数x临时创建一个形式参数a空间并将a空间的值置为x的值,其处理顺序是从右到左;执行函数f的函数体,计算形式参数a和b的值之和;为了返回结果,函数f再临时创建一个返回值空间,其值用a+b的值代替;函数体执行完成,准备返回之前,释放形式参数a和b空间,释放顺序是先a

27、后b,与创建的顺序相反;函数调用完成,回到主函数,将函数返回值赋值给变量z,然后释放函数f的返回值空间。上述调用函数的执行过程中临时创建了三个空间,其中有两个形式参数空间和一个返回值空间。单就形式参数空间而言,不仅要创建,而且要赋值,赋值的过程就是把一个空间的值拷贝到另一个空间。本例中的空间都是以整型为例,数值拷贝也只有4个字节。但如果形式参数的类型不是整型而是一个结构,且该结构体的大小有成千上万个字节,数值拷贝的过程所占用的时间资源就不能不考虑了。在学习C语言时,指针数据类型可以从一定程度上解决大结构参数传递所导致的低效问题,因为指针传递的是地址而非变量本身。C+中的引用类型同样可以解决参数

28、传递过程中的低效问题,而且在操作上比指针更方便,举例如下:【例1-8】 引用类型作为函数参数int getmax(int &a,int &b) return a>=b?a:b; void main()int x=10,y=20,z;z=getmax(x,y);注意函数getmax的两个形式参数a和b,都定义成引用的形式(而非指针。指针定义是*),且在主函数中调用getmax函数时,实在参数就是x和y,并未采用其他的特殊形式。采用引用参数后,函数调用时就没有参数传递的过程,没有用实在参数创建形式参数空间的过程。形式参数&a被定义为实在参数x的别名,形式参数&

29、b被定义为实在参数y的别名,函数体中的a就是实在参数x,b就是实在参数y。很显然,引用参数带来一个副作用:既然函数体中的a就是实在参数x,b就是实在参数y,那么在函数体中任何对“形式参数”a和b的修改都将直接影响到实在参数x和y,这在多数情况下是不允许的。为了避免这种情况的发生,C+提供了const约束:【例1-9】 带有const的引用参数int getmax(const int &a, const int &b) return a>=b?a:b; void main()int x=10,y=20,z;z=getmax(x,y);这样以来,一旦函数体中出现针对a和b的修

30、改,在编译层面上将会给出相应的错误提示。 函数返回引用通过对【例1-7】的分析得知,当函数定义有返回类型时,函数返回语句return将会创建一个返回值空间用来保存结果。只有当返回值被有效使用或者丢弃后才会释放该临时空间。与函数参数被设置为引用之后即可避免创建临时空间相似,如果一个函数返回一个引用,那么创建返回值空间的过程就可以避免。但由于引用必须“粘”在一个已经存在的空间之上,所以函数返回引用必须对应于已经存在的空间。【例1-10】 函数返回引用int vals100;int &put(int n) if (n>=0&&n<100) retur

31、n valsn; else return vals0; void main()put(0)=100;put(20)=50;cout<<vals0<<endl;cout<<vals20<<endl;当函数返回一个引用时,函数调用可以为赋值语句的左值。函数体中定义的局部变量不能作为引用返回,因为局部变量在函数返回之前将被释放,从而使得引用无所“依靠”。 引用的使用限制引用类型的使用具有一定的限制,包括:l 不能建立数组的引用,因为数组是一个由若干个元素所组成的集合,所以就无法建立一个数组的别名。l 引用是对某一变量或目标对象的引用,它本

32、身不是一种数据类型,因此引用本身不占存储单元,这样,就不能声明引用的引用,也不能定义引用的指针。l 不能建立空指针的引用,如:int &rp=NULL; 是错误的。l 不能建立空类型void的引用,如:不能建立void &ra=3; 因为尽管在C+语言中有void数据类型,但没有任何一个变量或常量属于void类型。1.4.3 new/delete在C+编程中,经常要用到动态存储空间分配和释放。new和delete提供了更加灵活的申请和释放方式。其实在C中也有类似的处理机制,与C+新增加的机制相比,使用难度较大。在C中,动态申请100个整型数据存储空间的方式如下:ptr=(int

33、 *)malloc(100*sizeof(int);而在C+中可以使用更加简单的方式:ptr=new int100;动态申请的存储空间需要及时释放。这一点不仅对于C,而且对于C+都需要引起重视。在C中的空间释放方式如:free(ptr);C+中的释放:delete ptr;/释放一个数组值得引起注意的是,在C中动态申请单一的变量也需要相同的方式处理。而在C+中就要简单得多,如下面的申请和释放的例子:p=new int;delete p;/释放单一空间无论按照C方式还是按照C+方式申请动态存储空间,返回的结果都是指针。在使用该空间之前,检查申请是否成功,即检查返回指针值是否为空(0值)是非常必要

34、的。而在释放之前,检查被释放的空间首地址(指针)是否为空也是一个良好的习惯。因此,在C+中,完整的申请、使用和释放的过程应该是:int *p;p=new int;if(p) *p=10; cout<<*p<<endl;delete p;而创建数组和释放数组的过程为:int *p;p=new int100;if(p) p10=10;/举例。可以是p0到p99 cout<<p10<<endl;delete p;/数组释放,需要有 标识1.4.4 函数相关在C语言中,函数作为程序结构的主体而存在。C+不仅沿袭了C中函数的特点,而且增加了一些函数新特性,

35、包括:内联函数、函数重载、函数的缺省参数等。作为函数,C语言中只有一种形式,那就是外部函数,所有的非主函数都与主函数处于并列的位置。而在C+中,除了外部函数之外,还有一种成员函数,是指函数作为类的成员而存在。因此,C+中的函数有两种形式:外部函数和成员函数。但无论是哪一种函数,其定义方式、函数定义中的元素以及函数调用的机制都是一样的。 内联函数内联函数是一种机制。其目的是为了提高代码的运行效率,减少由于函数调用所导致的额外系统开销。如果一个函数被定义为内联函数,则其代码层面是函数但其运行层面上不是函数。在学习C语言时已经知道,一个函数的调用过程需要经过以下几个步骤:系统自动保存函

36、数的返回地址(函数返回后运行的位置,压进堆栈);参数传递(自动创建形式参数空间,并用实在参数值填充形式参数空间);必要的现场保护(如内部寄存器保护等);函数内部局部变量创建;执行函数体;产生函数运行结果准备返回(创建返回值空间并赋值);释放局部变量空间;释放参数空间;恢复现场;返回。从中可以看出,除了真正核心部分执行函数体之外,其他部分都是为了处理函数调用而额外增加出来的。很显然,如果函数体部分足够小,以致于其执行时间远小于额外工作所占用的时间,就很有些得不偿失。如果不采用函数,直接将函数体部分嵌入到调用函数的位置上,将会极大地提高运行效率。但是,如果小型的函数体都直接嵌入,那么对于程序的维护

37、和修改是不利的,甚至会发生修改遗漏或错误的现象。采用函数机制就可以实现维护修改的完整性和正确性。那么,如何才能解决这两者之间的矛盾呢?既希望在代码修改层面上做到简单方便,又希望程序运行层面上不会导致由于函数调用而增加太多的额外系统开销,C+设计了函数的内联机制,即内联函数。内联函数是一种代码层面上是函数,但运行层面上不是函数的机制。是通过编译系统直接将内联函数进行处理以转换成嵌入式代码的机制。一个内联函数的定义,是通过在函数头上增加inline关键字而构成的,如【例1-11】所示。【例1-11】 一个内联函数的例子int getmax(int a,int b) return a>=b?a

38、:b; /一般函数inline void MyPrint(char *s) cout<<s; /内联函数void main() cout<<getmax(2,3); MyPrint("test");其中的MyPrint函数就被定义成一个内联函数。在逻辑上与下面的代码相同:int getmax(int a,int b) return a>=b?a:b;void main() cout<<getmax(2,3); cout<<"test"/函数体被嵌入一个函数被定义为内联函数,并不一定能够达到内联的效果。

39、编译系统还是要根据函数体的具体情况进行分析和判别。一般情况下,如果函数体中含有循环或者switch分支时将不能实现内联。对于外部函数,如果没有增加inline关键字,则一定不是内联。有了inline关键字,也不见得一定会成为内联。对于类中定义的成员函数,缺省认为含有inline关键字。详见第2章。 缺省值参数的函数C+规定,一个函数的参数是可以带有缺省值的。带有缺省值的参数,在函数调用时可以不提供实在参数,而直接以缺省值为准。如果提供了对应的实在参数,则以实在参数为准。【例1-12】 含有缺省值参数的函数void getlevel(int score,int minscore=6

40、0) if(score>=minscore) cout<<"及格"<<endl; else cout<<"不及格"<<endl;第二个参数minscore就带有一个缺省值。可采用以下方式调用:getlevel(80);/以60作为基准进行判断及格getlevel(50,36);/以36作为基准进行判断及格getlevel(85,90);/以90作为基准线进行判断不及格函数带有缺省值参数,可以简化函数调用的过程,但应注意,带有缺省值的参数,必须统一位于参数列表的右侧(后部)。如下面的定义就是错误的:in

41、t f2(int a, double x=1.2, char c, double y=2.1) ;这是因为第2个参数x有缺省值,但第3个参数反而没有缺省值。如果修改为:int f2(int a, char c, double x=1.2, double y=2.1) ;或者int f2(int a, double x, char c='A', double y=2.1);就是正确的了。 重载函数重载函数,是指一组函数名称相同但参数不同的函数。而所谓参数不同,既可以是参数的个数不同,也可以是参数的类型不同。【例1-13】 重载函数的例子intfa(int);doub

42、lefa(double);longfa(long);voidfb(int);voidfb(int,double);voidfb(double,int);三个fa函数构成重载关系,三个fb函数也构成重载关系。重载函数的每一个函数是独立的,各自可以有自己的定义,彼此没有任何关联关系。重载函数是C+中的新特性,在C语言中是没有这种特性的。这是因为在C语言中,函数名称作为全局名称而存在,是唯一区别一个函数的标志。而在C+中,代表一个函数的标志不再是函数名称,而是对函数名称及参数列表统一构造成的编码。因此,即便是函数名称相同,只要参数能够正确区分,就可以构成重载函数。在重载函数中,返回值不作为其中的因素

43、。一个函数是否有返回值,同名函数的返回值类型是否相同,都不构成是否重载的必要条件。带有缺省值参数的函数,对于函数的重载关系会构成一定的影响。这是因为一个带有缺省值参数的函数其本身就相当于多个函数的重载,如:int f2(int a, double x, char c='A', double y=2.1);本身相当于以下构成重载关系的三个函数:int f2(int a, double x);int f2(int a, double x, char c);int f2(int a, double x, char c, double y);所以,如果再构造一个函数f2,就不能再出现与

44、上述三个函数相同的参数列表。其实,判断一组函数是否能够构成重载,最简单的办法是构造调用用例。如果每一个函数调用都只唯一地对应一个函数定义,则重载成功。【例1-14】 判断下面两个函数能否构成重载关系。int fa(int);char fa(int, int a=5);两个函数同名,具备基本条件。给出一个调用用例fa(10,20);,唯一地对应到第二个函数。再给出一个调用用例fa(10);,将不仅可能对应到第一个函数,而且可以对应到第二个函数,因此,这两个函数不能构成重载关系。1.4.5 其他除了上述介绍的C+相关特性之外,还有几个与本书实例相关的内容。 单行注释C+除了可以采用C

45、中的块注释方法以外,还增加了一个单行注释功能。单行注释是以双正斜线开头,一直到本行结尾的注释。但应注意的是,如果在一个字符串内部存在双正斜线,则不能实现注释功能。本章前面的例子中已经用到了单行注释。 同名全局变量的使用在C语言中,如果在函数中定义了一个与全局变量同名的局部变量,则在函数中使用的该变量都是局部变量,而不能再使用同名全局变量。而在C+中,可以采用双冒号约束来使用同名的全局变量。【例1-15】 同名全局变量的使用int x=10;void f( ) int x=5; cout<< x << endl;/输出局部变量的值5 cout<<

46、 :x << endl;/输出全局变量的值10void main( ) f( ); 值得说明的是,双冒号约束只能针对全局变量,而不能针对外层空间的非全局变量。如:int x=10;void f( ) int x=5;/外层 int x=3;/内层 cout << x << endl;/输出3 cout << :x << endl;/输出10,而不是5 cout <<x << endl;/输出5 cout<< :x << endl;/输出10void main( ) f( ); 1.4.5

47、.3 声明C+与C一样,都遵循先定义后使用的原则。常见的例子就是函数。如果一个函数的使用在定义之前,则在使用之前应该给出函数原型。一个函数原型需要带有完整的返回值类型、函数名称和参数列表。其中参数列表中可以没有形式参数的名称,但必须给出参数的类型。对于一个带有缺省值参数的函数,如果必须在调用之前给出原型,那么应该在原型中给出形式参数的缺省值,在函数定义中不能再给出缺省值,如【例1-16】所示。【例1-16】 带有缺省值参数的函数原型void f ( int a, int y = 10);/原型中给出缺省值void main( )f(1);f(2,20);void f ( int a, int

48、y ) cout<<a+y<<endl; /函数定义中不能再重复给出参数的缺省值除了函数的原型声明之外,C+还增加了自定义类型的声明。这种自定义类型包括结构、联合(共同体)、类、枚举等。在此举一个结构的例子:【例1-17】 C+中的类型声明struct A int x; ;void main( )A a;/直接将A作为一种类型而声明。而在C中必须采用struct A a; 的形式a.x = 10;cout << a.x; constC+中的const关键字是一种常量的标志,可用于多种不同的应用场合,包括:定义带有类型的常量;常引用;限定函数参数

49、(在函数体中不可修改);常函数;类中的常成员;常指针;其他不允许修改的限定。用const定义一个常量的方式为:const int x=3; 此后的x被视作一个常量而不可修改。因此,在定义常量x的同时必须赋初值。常引用的定义形如:const int &j = i; 说明了不能通过j来修改i的值。在中提到,当一个函数的形式参数是引用类型时,在函数体中任何针对形式参数的修改都将直接修改了实在参数。为了避免非正常修改导致的错误,应对引用类型的形式参数进行const限定。具体形式形如:void f ( const int &a);常函数(常成员)通常应用于类定义中,详见第2

50、章中的相关内容。而常指针可以定义为const int *p = &i; 说明了在程序中将不能通过指针p修改指针所指向的对象i。因此,常指针一般也需要进行初始化。而如果用const限定一个函数的指针类型参数时,如:void f ( const int *p ); 则说明在函数体中将不允许修改指针p所指向的对象。实际上,const在C+中的应用非常广泛,也非常杂乱。关注重点,了解其他,才能逐步掌握其使用方法。1.5 本章小结本章是面向对象程序设计的基础内容,从其基本思想、概念和机制出发,引出对象(实体)、类以及类和类之间的关系,进而就C+基础给出了相应的介绍。学完本章之后,应对以下知识有所

51、了解:面向对象方法的基本思想归纳和演绎。归纳的过程是从实体(对象)到个性(特征)抽取,再到共性抽取的过程。演绎的过程是从共性到个性再到个体的过程。实体即为对象,包括物理对象和逻辑对象。特征集即为类,由静态特征和活动特征共同组成。静态特征称为类的数据成员,活动特征称为类的函数成员(或方法成员)。类的任何一个成员既可以是公有的,也可以是私有的。类和类之间的关系称为继承机制,可以是派生,也可以是组合。面向对象的三大机制包括封装、继承和多态。封装机制强调了类的数据和方法的封装性,同时也体现了类成员的私有特性。继承机制体现了由现有的类生成新类的方法。多态机制反映了类成员在继承过程中的使用灵活性。就C+的

52、基础方面,本章介绍的内容主要是为全书具体实例而铺垫的,涉及到控制台输出流、动态存储空间分配、引用、const以及与函数相关的特征。其中,函数重载、缺省参数、引用、new/delete作为本章重点;控制台输出的解释顺序(而不是输出顺序)、函数参数的传递顺序方面应引起必要的关注。第2章 类与对象类与对象是面向对象思想中的两个最基本的元素。本章以面向对象封装机制为基础,介绍类的定义、对象的创建及使用,以及与其相关的技术。2.1 类定义本节介绍类的定义,包括定义语法、类成员及其this指针。为了充分理解类成员的使用,在本节中适当地引入了定义对象和使用对象的例子。而关于定义和使用对象更详细的内容可参考2

53、.2。2.1.1 结构定义在C语言中,有一种构造类型称为结构。一个结构的基本形式如:struct S_PERSONchar name10;int age;其中,S_PERSON是结构的名称,name和age是结构成员,分别具有字符数组类型和整型类型。而C中使用结构的方法如:void main( )struct S_PERSON Zhang;/定义结构变量strcpy( Z, "Zhangsan");Zhang.age = 18;cout << Z << Zhang.age << endl;因为C+完整地

54、包含了C,所以上述程序段在C+开发环境中同样适用。在C+中对结构类型进行了扩展。扩展内容包括以下三个方面:不仅可以有数据成员,而且可以有函数成员;成员具有属性(对外可见性);结构变量声明时可省略struct关键字。如下例所示:【例2-1】 C+语言中的结构类型struct S_PERSONprivate:/私有成员声明,以下两个数据成员被定义为私有的,对外不可见char name20;int age;public:/公有成员声明,以下三个函数成员被定义为公有的,对外可见void set(char *n,int a)strcpy(name,n);age=a;/成员函数set的定义char *ge

55、tname()return name;/成员函数getname的定义int getage()return age;/成员函数getage的定义;/结构定义结束void main( )S_PERSON Zhang;/声明结构变量(对象),无需再使用struct关键字Zhang.set("Zhangsan",18);/调用对象的成员函数cout<<Zhang.getname( )<<Zhang.getage( )<<endl;/调用对象的成员函数因为两个数据成员被定义为私有的,所以Z或者Zhang.age的用法都是错误的,只

56、能使用结构中定义的公有成员函数。2.1.2 类定义与结构相同,类也是C+中的一种构造类型。如果将【例2-1】中的struct关键字直接修改为class,就是一个典型的类定义和使用的例子。为了进一步说明类的定义,有以下的例子:【例2-2】 类定义与使用class A/class为关键字,A为自定义类名private:/定义私有成员int x;/数据成员int y;/数据成员public:/定义公有成员void set(int a,int b)x=a; y=b;/函数成员(方法成员)int getx( )return x;/函数成员(方法成员)int gety( )return y;/函数成员(方法成员);/类定义结束,以分号结束void main( )A a;/创建对象a.set(10,20);/使用对象,调用对象的set函数cout<<a.getx()<<","<<a.gety()<<endl;/调用对象的getx函数和gety函数对于类的定义及其相关问题进一步说明如下:任何一个类定义,都是以关键字class开头,并对类唯一命名。类定义体以花括号括起来,最后以分号结束。类定义体中可以只有数据成员,也可以只有方法成员,或者两者都有。成员顺序不限。任何一个成员,都可以标注其属性(对外可见性)。属性可以是私有(pr

温馨提示

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

评论

0/150

提交评论