面向对象课件_第1页
面向对象课件_第2页
面向对象课件_第3页
面向对象课件_第4页
面向对象课件_第5页
已阅读5页,还剩438页未读 继续免费阅读

下载本文档

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

文档简介

面向对象的设计方法面向对象的C++程序设计基础21.1面向对象的思想一、面向对象的含义

“面向对象”是软件设计的一种新思想,旨在使人们分析、设计系统的方法更接近认识系统的方法;

面向对象方法就是为计算机软件的创建提出了一种模型化世界的抽象方法;软件工程专家给出了面向对象的描述:

面向对象

=对象

+分类

+继承

+消息通信

即面向对象就是使用对象、类、继承等机制,对象之间仅能通过消息的传递实现通信。3

二、面向对象的程序设计

面向过程的程序设计

基本思想:模块分解和功能抽象,复杂的程序必然包含一组数据以及用来处理这些数据的函数,但函数是独立的。程序自身不清楚哪些函数处理了这些数据,即数据与处理这些数据的操作相分离。缺点是:1、程序修改麻烦:例如程序修改了一个变量名,则要修改所有使用该变量的语句;2、可靠性差,一致性难以保证:数据没有封装,数据可能被分散于程序各处的函数改变,数据从整体上很难保证可靠和一致。

面向对象的思想4

面向对象的程序设计

基本思想:将客观世界抽象为多个对象,将要设计的系统表示为对象的集合;对同类对象抽象出其共性,形成类;类是封装了“数据和处理这些数据的操作”的一个整体,对象是类的实例;类通过外部接口与外界联系,对象之间通过消息进行通讯。

类和对象是实现数据封装的具体机制,通过这种机制,把数据以及与其相关的操作进行封装(或捆绑),外界不能随意修改和任意使用这些量,只有满足一定的访问权限才能进行访问操作,增强了数据的安全性(解决了数据与操作相分离)。二、面向对象的程序设计面向对象的思想5面向对象的基本概念

——对象一般意义上的对象:是现实世界中一个实际存在的事物。可以是有形的(比如一辆汽车),也可以是无形的(比如一项计划)。是构成世界的一个独立单位,具有:静态特征:可以用某种数据来描述动态特征:对象所表现的行为或具有的功能6面向对象的基本概念

——对象面向对象方法中的对象:是系统中用来描述客观事物的一个实体,它是用来构成系统的一个基本单位。对象由一组属性和一组行为构成。属性:用来描述对象静态特征的数据项。行为:用来描述对象动态特征的操作序列。7一、现实世界中的对象对象是现实世界的一个实体,它具有如下特征:1、有一个名字以区别于其他对象;2、有一个状态用来描述它的某些特征;3、有一组操作,每一个操作决定了对象的一种功能或行为;对象的操作分为两类:1)自身所承受的操作。2)是它施加于其它对象的操作。(例如人可以接受知识,人可以驾驶汽车)面向对象的基本概念8二、计算机世界中的对象在计算机世界中,可以把对象看成是存储器中一个可标识的区域,它能保存固定或可变数目的数值(或数值的集合),这些数值代表对象的属性数据和对象的成员函数代码。从软件形式上看,对象是系统程序员、应用程序员或用户所定义的抽象数据类型的变量。面向对象的基本概念9对象的状态指对象自身所具有的那些特征(属性);这些状态的存在,使对象能对自身以及对外界对象施加操作;对象的状态并不是完全用来直接为外界服务的,但它们本身是能够为外界服务的基础。

对象的状态面向对象的基本概念10模块独立性对象是独立存在的模块,封装了数据和操作。外部使用时只需了解它具有哪些功能。模块间的依赖性极小或几乎没有。动态连接性对象之间有联系,通过消息激活机制,把一个个对象动态地连接在一起。易维护性对象修改、功能完善及实现的细节都被局限于对象的内部,不会涉及到外部。

对象的特性面向对象的基本概念11面向对象的基本概念

——类分类——人类通常的思维方法分类所依据的原则——抽象忽略事物的非本质特征,只注意那些与当前目标有关的本质特征,从而找出事物的共性,把具有共同性质的事物划分为一类,得出一个抽象的概念。例如,石头、树木、汽车、房屋等都是人们在长期的生产和生活实践中抽象出的概念。12面向对象的基本概念

——类面向对象方法中的"类"具有相同属性和服务的一组对象的集合为属于该类的全部对象提供了抽象的描述,包括属性和行为两个主要部分。类与对象的关系:

犹如模具与铸件之间的关系,一个属于某类的对象称为该类的一个实例。面向对象的基本概念13

类与对象的关系类(模板)与对象(实例)的关系是抽象与具体的关系;同一类的不同实例之间,必有如下特点:相同的操作集合;相同的属性集合;不同的对象名。面向对象的基本概念14

类的确定与描述类的确定采用归纳法,基于对所遇到对象的总体分析中,归纳出其共同的特征来定义类;类是数据和对这些数据进行操作的函数的封装体,它是一种同时含有数据和函数的构造数据类型(结构);类的描述同时包括数据和函数定义,类中的数据和函数称为类的成员(数据成员、成员函数);数据成员是类的那些成员函数需要用到的变量,成员函数是对数据成员进行处理的程序段。面向对象的基本概念15面向对象的基本特性

——封装性把对象的属性和服务结合成一个独立的系统单元。尽可能隐蔽对象的内部细节。对外形成一个边界(或者说一道屏障),只保留有限的对外接口使之与外部发生联系。16

什么是封装性?在面向对象中,封装是将一段程序代码“包装”起来,只需知道这段“代码”完成的功能,而不必关心功能的实现细节;“代码”包括一组数据和与这组数据相关的操作;封装是指将一组数据(对象属性)和与这组数据相关的操作(对象服务)集合在一起,形成一个不可分割、独立的实体—对象,用户不必知道对象行为的实现细节,只需根据对象提供的外部接口访问对象的服务。面向对象的基本特性17

1.有一个清楚的边界,对象的所有私有数据、内部程序(成员函数)细节都被限定在这个边界内;

封装应该具有以下几个条件:2.至少有一个接口,这个接口描述了该与其它对象之间的相互作用、请求和响应,它就是消息;3.对象行为的内部实现代码受封装壳的保护,其他对象不能直接修改该对象所拥有的数据和相关程序代码。面向对象的基本特性18

面向对象系统的封装性

面向对象系统的封装性是一种信息隐藏技术。它的目的在于将对象的使用者与设计者分开,使用者不必知道对象行为实现的细节;

封装机制提供了一种共享代码的手段。通过封装,把一段代码定义在一个类中,并且在另一个类所定义的操作中,可以通过创建该类的实例,并向它发送消息而启动这段程序代码,达到共享代码的目的。面向对象的基本特性19面向对象的基本特性

——继承性继承对于软件复用有着重要意义,是面向对象技术能够提高软件开发效率的重要原因之一。定义:特殊类的对象拥有其一般类的全部属性与服务,称作特殊类对一般类的继承。例如:将轮船作为一个一般类,客轮便是一个特殊类。201、继承表达了对象之间的相交关系,即某类对象可以继承另一类对象的特征和能力。具有继承关系的类有如下特征:☆类间具有共享特征(包括数据和程序代码共享);☆类间具有细微的差别或新增部分(包括非共享的程序代码和数据);☆类间具有层次结构(包含关系)。2、继承的作用:在定义子类时不必重复在父类中定义的数据和操作,可减少代码冗余,提高软件复用性;增强一致性,减少模块之间的接口和界面,易于程序维护。

继承的含义面向对象的基本特性21

继承的分类

从继承源上可划分为单继承和多继承;

单继承:一个类只允许有一个父类时,这种类的继承为单继承;多继承:一个类同时允许有2个以上的父类时,这种类的继承为多继承。面向对象的基本特性22

单继承与多继承ABCDE单继承多继承FEDCBA23

面向对象系统的继承性能清晰体现相关类之间的层次结构关系;能减小代码和数据的冗余度,增加程序重用性;能通过增强一致性来减少模块间的接口和界面,增加程序的易维护性;继承是能自动传播代码的有力工具;继承还是在一些比较一般的类的基础上进行构造、建立和扩充新类的最有效的手段,利于软件设计的逐步细化,即先进行公共特性的设计,再自顶向下开发子类,逐步加入新内容。24面向对象的基本特性

——多态性多态是指在一般类中定义的属性或行为,被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为。这使得同一个属性或行为在一般类及其各个特殊类中具有不同的语义。例如:数的加法->实数的加法

->复数的加法25

多态性的含义多态性(Polymorphism)是指同一名字(函数、运算符)在不同的场合具有不同的语义,或者同一界面有多种实现;C++中的多态性是指不同的对象收到相同的消息时产生不同的动作或行为;C++支持两种多态性:

1、编译时的多态性:通过函数重载来实现。

2、运行时的多态性:通过虚函数来实现。面向对象的基本特性26

重载(Overloading)C++允许为已有的函数和运算符重新赋予新的含义,即具有相同名字的函数和运算符在不同的场合可以表现为不同的行为;重载的含义是指通过为函数和运算符创建附加定义而使它们的名字重载,即相同名字的函数或运算符在不同的场合可以表现出不同的功能;函数重载:同一个作用域内若干个参数特征不同的函数可以使用相同的名字,定义重载函数时,函数名相同,函数参数类型、个数、顺序必须不同;运算符重载:同一个运算符可以施加在两个任意类型的操作数(例如结构变量或对象)上。27

虚函数虚函数是指在类等级的不同层次中说明的名字、参数特征和返回值类型都相同但实现算法不同的函数;虚函数使用户在一个类等级中可以使用相同函数的多个版本,每个版本均属于类等级中不同的类,究竟使用的是哪一个特定的版本需要在运行中决定;虚函数的各个版本中,其返回值、函数参数的个数和类型必须一致。28计算机程序计算机的工作是用程序来控制的程序是指令的集合。指令是计算机可以识别的命令。计算机语言的发展29机器语言与汇编语言由计算机硬件系统可以识别的二进制指令组成的语言称为机器语言。计算机发展的初期,软件工程师们只能用机器语言来编写程序。这一阶段,在人类的自然语言和计算机编程语言之间存在着巨大的鸿沟。汇编语言将机器指令映射为一些可以被人读懂的助记符,如ADD、SUB等。此时编程语言与人类自然语言间的鸿沟略有缩小,但仍与人类的思维相差甚远。因为它的抽象层次太低,程序员需要考虑大量的机器细节。计算机语言的发展30高级语言高级语言屏蔽了机器的细节,提高了语言的抽象层次,程序中可以采用具有一定涵义的数据命名和容易理解的执行语句。这使得在书写程序时可以联系到程序所描述的具体事物。计算机语言的发展31面向对象的语言出发点:更直接地描述客观世界中存在的事物(对象)以及它们之间的关系。特点:是高级语言。将客观事物看作具有属性和行为的对象。通过抽象找出同一类对象的共同属性和行为,形成类。通过类的继承与多态实现代码重用。计算机语言的发展32面向对象的语言优点:使程序能够比较直接地反问题域的本来面目,软件开发人员能够利用人类认识事物所采用的一般思维方法来进行软件开发。计算机语言的发展33

LISP语言:以表处理为特色,是一种人工智能语言。

Simula67语言:引入了几个面向对象程序设计语言中的重要概念和特性,即数据抽象、类机构和继承机制。

SmallTalk语言:源于Simula语言,体现了纯粹的面向对象程序设计思想,是真正的面向对象语言。

C++语言:1980年AT&T设计了混合型的面向对象程序设计语言C++,在C的基础上扩展了类、对象等机制,并兼容C语言,应用最为广泛。面向对象的语言计算机语言的发展34程序设计方法的发展历程

——面向过程的程序设计方法程序的目的:用于数学计算主要工作:设计求解问题的过程缺点:对于庞大、复杂的程序难以开发和维护35程序设计方法的发展历程

——面向过程的结构化程序设计方法设计思路自顶向下、逐步求精。采用模块分解与功能抽象,自顶向下、分而治之。程序结构:按功能划分为若干个基本模块,形成一个树状结构。各模块间的关系尽可能简单,功能上相对独立;每一模块内部均是由顺序、选择和循环三种基本结构组成。其模块化实现的具体方法是使用子程序。36程序设计方法的发展历程

——面向过程的结构化程序设计方法优点:有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子任务,便于开发和维护。面向对象的方法37程序设计方法的发展历程

——面向过程的结构化程序设计方法缺点:可重用性差、数据安全性差、难以开发大型软件和图形界面的应用软件把数据和处理数据的过程分离为相互独立的实体。当数据结构改变时,所有相关的处理过程都要进行相应的修改。每一种相对于老问题的新方法都要带来额外的开销。图形用户界面的应用程序,很难用过程来描述和实现,开发和维护也都很困难。38程序设计方法的发展历程

——面向对象的方法将数据及对数据的操作方法封装在一起,作为一个相互依存、不可分离的整体——对象。对同类型对象抽象出其共性,形成类。类通过一个简单的外部接口,与外界发生关系。对象与对象之间通过消息进行通讯。39程序设计方法的发展历程

——面向对象的方法优点:程序模块间的关系更为简单,程序模块的独立性、数据的安全性就有了良好的保障。通过继承与多态性,可以大大提高程序的可重用性,使得软件的开发和维护都更为方便。40面向对象的软件工程面向对象的软件工程是面向对象方法在软件工程领域的全面应用。它包括:面向对象的分析(OOA)面向对象的设计(OOD)面向对象的编程(OOP)面向对象的测试(OOT)面向对象的软件维护(OOSM)面向对象的软件开发41系统分析系统分析阶段应该扼要精确地抽象出系统必须做什么,但是不关心如何去实现。面向对象的系统分析,直接用问题域中客观存在的事物建立模型中的对象,对单个事物及事物之间的关系,都保留他们的原貌,不做转换,也不打破原有界限而重新组合,因此能够很好地映射客观事物。面向对象的软件开发42设计针对系统的一个具体实现运用面向对象的方法。其中包括两方面的工作:把OOA模型直接搬到OOD,作为OOD的一部分针对具体实现中的人机界面、数据存储、任务管理等因素补充一些与实现有关的部分。面向对象的软件开发43编程OOP工作就是用一种面向对象的编程语言把OOD模型中的每个成份书写出来,是面向对象的软件开发最终落实的重要阶段。面向对象的软件开发44测试测试的任务是发现软件中的错误。在面向对象的软件测试中继续运用面向对象的概念与原则来组织测试,以对象的类作为基本测试单位,可以更准确的发现程序错误并提高测试效率。面向对象的软件开发45维护将软件交付使用后,工作并没有完结,还要根据软件的运行情况和用户的需求,不断改进系统。使用面向对象的方法开发的软件,其程序与问题域是一致的,因此,在维护阶段运用面向对象的方法可以大大提高软件维护的效率。面向对象的软件开发46C++语言的产生C++是从C语言发展演变而来的,首先是一个更好的C引入了类的机制,最初的C++被称为“带类的C”1983年正式取名为C++从1989年开始C++语言的标准化工作于1994年制定了ANSIC++标准草案于1998年11月被国际标准化组织(ISO)批准为国际标准,成为目前的C++C++的初步知识47C++的特点全面兼容C它保持了C的简洁、高效和接近汇编语言等特点对C的类型系统进行了改革和扩充C++也支持面向过程的程序设计,不是一个纯正的面向对象的语言支持面向对象的方法C++的初步知识48从C到C++最简单的C++程序C++程序的构成和书写格式C++程序的编写和实现C++(Cplusplus)是C语言的超集,完全兼容C语言,但在概念上不同于C,应按C++自己的方式来使用它,掌握C++的思维方式、程序设计方法和编程习惯。那么,C++对C做了哪些修改和增强?C++的初步知识49函数原形说明变量的说明输入输出const说明符void类型从C到C++C++的初步知识50

函数原形说明C++要求为函数提供完全的原型,包括所有参数的类型和返回值类型,目的在于:编译系统可以对函数的定义和使用之间的一致性进行严格的检查,保证函数使用的正确性;在函数定义和声明时,必须提供全部信息:

voiddisplay(int,char*,float);intmixture(int,float,char*);C++的初步知识51

变量的说明在C中,变量的定义必须出现在函数或复合语句的最前面,在正常执行语句后面不能再定义变量。intm,n,k;m=10;n=9;k=m*n;….在C++中,变量的定义可以出现在任何位置。intm,n,k;m=10;n=9;k=m*n;….intx,y;y=k+m;…C++的初步知识52

输入和输出C++中除了保留C标准库中的各种输入和输出函数外,还提供了一套新的输入和输出函数—流式输入输出。使用时包含头文件“iostream.h”。例:

#include<iostream.h>intx,y;floatz;cin>>x>>y>>z;//从标准终端输入数据

char*str=“Hello,howareyou!”;cout<<“Pleaseoutputtheinfoofthestring:”<<str<<“\n”;//向屏幕输出字符串53

const说明符定义常量时,C语言用#define,而C++用const说明符,const在C++中的作用:1.代替#define宏定义常量例:#defineMAX100

可替代为:

constintMAX=100;或intconstMAX=100;2.定义常量数组

constfloatdata[]={1.2,2.5,3.6,4.8,5.5};C++的初步知识54

const说明符3.

const说明符与函数参数相连接。例:voidprint_value(constintvalue){cout<<value;}

试图在该函数体中改变const参量value的是非法的。C++的初步知识55

const说明符4.定义指针常量,根据const位置的不同含义也不同,有三种方式:(1)指向常量的指针:说明一个指针变量指向的数据是常量。(name是指向字符串常量的指针,name指向的字符串不能更改,但name可以更新)

const

char*name=“Richard”;name=“Martin”;对

name[0]=‘C’;错C++的初步知识56

const说明符(2)常指针:把指针本身声明为一个常量,而不是将它指向的对象声明为常量。(name不能更改,name指向的内容可更新)

char*constname=“Richard”;name=“Martin”;错

name[0]=‘C’;对(3)指向常量的常指针:

这个指针本身不能改变,它指向的值也不能改变。(name不能更改,name指向的内容也不能更新)

constchar*constname=“Richard”;name=“Martin”;错

name[0]=‘C’;错57

void类型void是指没有数值的数据类型,没有任何返回值的函数应说明为void类型。例如:voidfun(int*);void也可表示一个函数不需要任何入口参数。例如:intgraph(void);将C程序转换成与C++兼容的最常见的工作就是将没有返回值的函数说明为void类型。C++严格检查所有的函数原型是否包括:返回值类型、函数名和各个参数的类型。C++的初步知识58例1.4.1输出一行字符:“ThisisaC++program.”程序如下:#include<iostream>//用cout输出时需要用此头文件usingnamespacestd;//使用命名空间std

intmain(){

cout<<“ThisisaC++program.\n”;//用C++的

//方法输出一行信息

return0;}程序运行时输出:ThisisaC++program.最简单的C++程序#include<iostream.h>59命名空间(Namespace)一个命名空间将不同的标识符集合在一个命名作用域(namedscope)内为了解决命名冲突例如,声明一个命名空间NS:namspaceNS{classFile;voidFun();}

则引用标识符的方式如下,NS::Fileobj;NS::Fun();没有声明命名空间的标识符都处于无名的命名空间中60命名空间(Namespace)可以用using来指定命名空间例如,经过以下声明:

usingNS::File;

在当前作用域中就可以直接引用Fileusingnamespacestd;

命名空间std中所有标识符都可直接引用在新的C++标准程序库中,所有标识符都声明在命名空间std中,头文件都不使用扩展名61最简单的C++程序例1.4.2求a、b两个数之和。//求两个数之和(本行是注释)#include<iostream>//用cout输出时需要用此头文件usingnamespacestd;//使用命名空间std

intmain()//主函数首部{inta,b,sum;//定义变量

cin>>a>>b;//输入语句

sum=a+b;

cout<<“a+b=”<<sum<<endl;//输出语句

return0;}注:1825(输入18和25给a、b),a、b之间一个空格62C++程序的书写格式一个C++程序可以由一个程序单位或多个程序单位构成。每一个程序单位作为一个文件,每个文件由若干个函数组成。由函数构成的C++程序中,必须只有一个主函数main(),它是程序执行的入口。需要指出两点:1、C++函数(包括main)的定义和声明,必须提供函数的全部信息:返回值、函数参数类型和个数。无返回值或无参的情况用void;2、注释形式:在一行语句内,“//”后面的字符被视为注释在多行语句内,“/*”和“*/”中间的所有字符被视为注释。63C++程序的编写和实现用C++语言编写源程序*.cpp;对源程序进行编译,形成*.obj文件。如果编译出错,再修改源程序,直到编译正确;将目标文件与C++编译器提供的库文件进行连接,形成可执行的二进制文件*.exe;运行程序;分析运行结果。642.1类的声明和对象定义C++类及其对象的封装性类是具有相同属性和行为的一组对象的集合,它为属于该类的全部对象提供了统一的抽象描述,其内部包括属性和行为两个主要部分。利用类可以实现数据的封装、隐藏、继承与派生。利用类易于编写大型复杂程序,其模块化程度比C中采用函数更高。652.1类的声明和对象定义C++类及其对象的封装性类是对象共性特征的抽象,对象的类型称为类,类是对象的模板,对象是类的具体实例;先声明一个类,再定义对象,类是抽象的,不占用内存,对象是具体的,占用实际的存储空间;可以看作是一种特殊的构造数据类型,是结构体的扩充形式;类是数据和对这些数据进行操作的函数的封装体,是包括数据和函数的数据类型;类的定义包括数据和函数的定义,类中的数据和函数都是类的成员;在C++中,类用class来构造。66类的声明

//声明结构体类型struct

Student{intnum;

charname[20];

charsex;

};//定义2个结构变量Studentstud1,stud2;class

Student//声明类,以class开头

{intnum;

charname[20];

charsex;//以上3行是数据成员

voiddisplay()//成员函数

{cout<<“num:”<<num<<endl;

cout<<“name:”<<name<<endl;

cout<<“sex:”<<sex<<endl;}};

//定义Student类的对象stud1,stud2Student

stud1,stud2;672.1类的声明和对象定义上述类定义中未限定成员的访问属性,对象stud1的数据和函数都是private的,数据安全了。但外界不能调用stud1中的函数和功能,因为没有提供类的对外的接口,类有何用?不能把类中的全部成员与外界隔离,一般将类中的数据隐藏起来,声明为私有的(private),把成员函数作为对外界的接口,声明为公有的(public)。例如,上述类声明改为:classStudent//声明类类型{private://声明以下部分为私有

intnum;

charname[20];

charsex;

public://声明以下部分为公有

voiddisplay(){cout<<“num:”<<num<<endl;

cout<<“name:”<<name<<endl;

cout<<“sex:”<<sex<<endl;}};Student

stud1,stud2;68在C++中,定义类的一般格式为:class

类名{

private:

//私有类型只限于通过自己的成员函数

//来访问,即只有类本身能够访问它数据成员和成员函数说明

public:

//公有类型提供了类的外部接口,允许类

//的使用者来访问它数据成员和成员函数说明

protected:

//保护类型只允许本类成员函数或派生

//类成员函数访问,用于类的继承和派生数据成员和成员函数};69公有类型成员在关键字public后面声明,它们是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。类的声明和对象的定义70私有类型成员在关键字private后面声明,只允许本类中的函数访问,而类外部的任何函数都不能访问。如果紧跟在类名称的后面声明私有成员,则关键字private可以省略。类的声明和对象的定义71保护类型与private类似,其差别表现在继承与派生时对派生类的影响不同。类的声明和对象的定义72在类的定义中应注意以下几点:1、“类名”是一个合法的标识符;2、关键字private,public和protected说明类成员的三种访问权限;3、{}以内的部分是“类内”,{}以外的部分为“类外”;4、类定义的声明部分在“类内”,类的成员函数既可以在“类内”定义,也可以在“类外”定义;5、在“类内”不允许对所声明的数据成员进行初始化,类的数据成员的类型可以任意。73classCArea//声明一个类{private://私有部分

intx,y,area;public://公有部分

voidsquarea(intvx,intvy);};例题2.1.174类的成员classClock{public:

voidSetTime(intNewH,intNewM,

intNewS);

voidShowTime();private:intHour,Minute,Second;};成员数据成员函数类的声明和对象的定义75voidClock::SetTime(intNewH,intNewM,

intNewS){Hour=NewH;Minute=NewM;Second=NewS;}voidClock::ShowTime(){cout<<Hour<<":"<<Minute<<":"<<Second;}类的声明和对象的定义76数据成员与一般的变量声明相同,但需要将它放在类的声明体中。类的声明和对象的定义77成员函数在类中说明原形,可以在类外给出函数体实现,并在函数名前使用类名加以限定。也可以直接在类中给出函数体,形成内置成员函数。允许声明重载函数和带默认形参值的函数类的声明和对象的定义78类和结构体的异同1、均为构造类型;2、结构体用struct、类用class作为标识;2、结构体中的成员只有数据成员,且访问权限默认为public的;3、类的成员包括数据和函数,且访问权限可以设置为private、public和protected的;4、结构没有实现数据封装,数据可以被任何外部函数访问。类实现了数据封装,可以将数据限定为只能被本身的成员函数访问。类的声明和对象的定义79定义对象的方法1、先声明类类型,然后再定义对象:如Student

stud1,stud2;在声明类类型之后,定义对象有2种方法:class类名对象名;如classStudentstud1,stud2;类名对象名;如Student

stud1,stud2;类的声明和对象的定义80定义对象的方法2、在声明类类型的同时定义对象:classStudent

//声明类类型{public://先声明公用部分

voiddisplay()

{cout<<“num:”<<num<<endl;

cout<<“name:”<<name<<endl;

cout<<“sex:”<<sex<<endl;}

private://后声明私有部分

intnum;

charname[20];

charsex;}stud1,stud2;//定义Student类的对象stud1,stud2类的声明和对象的定义81定义对象的方法3、不出现类名,直接定义对象:class

//声明类类型{public://声明公用部分

……

private://声明私有部分

……

protected://声明保护部分

……}stud1,stud2;//定义对象stud1,stud2

定义多个对象时,对象名之间用逗号隔开;对象名可以是一般的对象、指向对象的指针或引用名,也可以是对象数组。

类的声明和对象的定义82例2.1.2定义一个日期类CDate,再创建一个生日对象。classCDate{intyear;

intmonth;

intday;public:

voidSetDate(int,int,int);

voidShowDate();}myBirthday;//同时创建对象myBirthday或者定义类之后:CDate

myBirthday,*p,q[5];类的声明和对象的定义83

2.2

类的成员函数C++类及其对象的封装性类的成员函数具有一般函数的性质,它同时属于某个类的成员,出现在类定义体中,可被限定为具有private、public和protected三种访问属性;成员函数可以访问本类中的任何成员(包括private和public特性),可以引用在本作用域中有效的数据;private属性的成员函数只能被本类其它的成员函数调用,不能被外界调用;public属性的成员函数可以被外界调用,它是类的对外接口。84在类外定义成员函数类的成员函数类的成员函数可以在“类内”声明,在“类外”定义;类外定义成员函数的一般形式如下:

返回值类型

类名::成员函数名(参数表){…

…//函数体

}

其中::是作用域运算符。85例2.2.1classStudent

{public:

voiddisplay();//公用成员函数原型声明

private:

intnum;

stringname;

charsex;//以上3行是私有数据成员};voidStudent

::display()//在类外定义成员函数{cout<<“num:”<<num<<endl;

cout<<“name:”<<name<<endl;

cout<<“sex:”<<sex<<endl;}Student

stud1,stud2;//定义2个类对象类的成员函数86内置(inline)成员函数有些简单的成员函数可以直接在类定义中定义,在类定义体中定义的成员函数称为内置函数(内联函数);内置函数可以减少函数调用时的内存开销,不用记录函数调用返回地址,只将代码嵌入到函数调用点。内置函数的定义有两种方法:1、隐式定义将成员函数的定义体放在“类内”中。2、显式定义在“类内”声明,在“类外”定义时在函数名前加关键字inline。类的成员函数87内置成员函数举例(一)classPoint{public:voidInit(intinitX,intinitY)

{X=initX;Y=initY;}intGetX(){returnX;}intGetY(){returnY;}private:intX,Y;};类的成员函数88内置成员函数举例(二)classPoint{public:voidInit(intinitX,intinitY);intGetX();

intGetY();

private:intX,Y;};类的成员函数inlinevoidPoint::

Init(intinitX,intinitY){X=initX;Y=initY;}inlineintPoint::GetX(){returnX;}inlineintPoint::GetY(){returnY;}90成员函数的存储方式定义类对象时,系统会为每个对象分配存储空间,包括为类对象的数据和函数代码分配空间;如果定义同类的10个对象,那么如何为这10个对象分配不同的空间?10个同类对象占用存储单元的情况类的成员函数91成员函数的存储方式为了节约内存,只用一段存储空间来存放10个对象拥有的共同的函数代码段,如下图:

10个同类对象成员函数的存储方式C++规定:对象占用的存储空间只是对象数据成员占用的存储空间,可用sizeof函数验证;共同的成员函数代码存储在对象空间之外。类的成员函数92成员函数的存储方式调用不同对象的成员函数时都是执行同一段函数代码,但其执行结果一般不同(常与数据有关);不同对象使用同一段函数代码,它们如何对不同对象中的数据进行操作呢?C++专门为此设计了一个this指针,用来指向不同的对象;调用哪个对象,this就指向哪个对象,就访问它的成员。类的成员函数93类中成员的访问方式类中成员互访直接使用成员名类外访问使用“对象名.成员名”方式访问

public

属性的成员类的成员函数94例:类的应用举例#include<iostream>usingnamespacestd;classClock{//类的声明略}//类的实现略voidmain(void){ClockmyClock;

myClock.SetTime(8,30,30);

myClock.ShowTime();}类的成员函数95对象成员的引用访问对象中的成员有3种方法:1、通过对象名和成员运算符访问对象中的成员,即:

对象名.数据成员名

or对象名.成员函数名(参数表)例如:stud1.num=1001;

stud1.display();

//设num和display函数为public成员

类的成员函数96对象成员的引用定义一个日期类,使其具有输出当前日期的功能。见p-27例2.3.1:引用对象的成员2、通过指向对象的指针访问对象中的成员,即:对象指针->数据成员名

or对象指针->成员函数名(参数表)

例如:Timet,*p=&t;cout<<p->hour;//hour是t中的成员3、通过对象的引用变量访问对象中的成员。类的成员函数97类的封装性类将一组数据和操作这些数据的算法封装在自定义的抽象数据类型中。一般将类的所有数据声明为private(与外界隔离),将成员函数声明为public(外界可以调用),外界通过调用公用成员函数实现对数据的操作;公用成员函数是用户使用类的公用接口(publicinterface),用户只需知道如何调用公用成员函数,不需了解函数的内部实现细节,此即接口与实现分离;通过成员函数对数据成员进行操作称为类的实现。为了防止用户随意修改公用成员函数,一般不向用户公开公用成员函数的源代码,用户只能接触到其目标代码;类中被操作的数据是私有的,实现的细节对用户隐蔽,此即私有实现(privateimplementation)。“类的公用接口与私有实现的分离”形成了信息隐蔽。

类的封装和信息隐藏98封装性的好处软件工程的一个最基本的原则就是将接口与实现分离,信息隐蔽是软件工程的重要概念,其好处在于:(1)如果想修改或扩充类的功能,只需修改本类中有关的数据成员和与它有关的成员函数,程序中类外的部分可以不必修改。(2)如果在编译时发现类中的数据读写有错,不必检查整个程序,只需检查本类中访问这些数据的少数成员函数。这样,就极大方便了程序(特别是大程序)的设计、修改和调试。类的封装和信息隐藏99类声明和成员函数定义的分离类被一个程序使用,将类的声明和成员函数的定义写在文件开头;类被多个程序使用,需要重复将其写在多个文件开头;将类的声明(包含成员函数的声明)放在指定的头文件中,用户使用类时,只要包含相应的头文件,即可定义该类的对象、调用对象的公用成员函数;为了实现信息隐蔽,将类的成员函数定义放在另一个文件中。这时,一个程序有两个模块:类的成员函数定义文件student.cpp和主模块main.cpp,它们都包含类声明头文件student.h;将student.cpp编译成目标文件student.obj,形成用户类库,与main.obj和系统资源库链接而成main.exe。类的封装和信息隐藏100类声明和成员函数定义的分离//student.hclassStudent{public:

voiddisplay();

private:

intnum;

charname[20];

charsex;};类的封装和信息隐藏101类声明和成员函数定义的分离//student.cpp#include<iostream.h>#include“student.h”voidStudent::display()

{cout<<“num:”<<num<<endl;cout<<“name:”<<name<<endl;cout<<“sex:”<<sex<<endl;}//main.cpp#include<iostream.h>#include“student.h”intmain(){Student

stud;

stud.Display();

return0;}类的封装和信息隐藏102类声明和成员函数定义的分离图2.4.1包含多个源文件程序的编译和连接过程类的封装和信息隐藏103类声明和成员函数定义的分离使用类库将减少用户对类和成员函数定义的工作量;类库包括两个部分:(1)类声明头文件;(2)已经编译过的成员函数定义文件-目标文件;用户只需将类库装入到自己的计算机系统(装在C++编译系统所在的子目录),并在程序中用#include命令将有关类声明的头文件包含到程序中,即可使用这些类和其中的成员函数了;接口与实现的分离,为软件开发商给用户提供类库创造了条件,而类实现的程序代码得到了保护。类的封装和信息隐藏104构造函数构造函数的作用是在对象被创建时使用特定的值构造对象,或者说将对象初始化为一个特定的状态。在对象创建时由系统自动调用。如果程序中未声明,则系统自动产生出一个默认形式的构造函数允许为内置函数、重载函数、带默认形参值的函数构造函数和析构函数105构造函数构造函数和析构函数构造函数的名字必须与类名相同;构造函数一般声明为public,无返回值,无需定义返回类型;构造函数是系统自动调用的,且执行一次;构造函数不能被继承,但允许重载(overloading);构造函数的功能是对对象进行初始化,一般构造函数用于对象的初始化,每当对象被声明时对数据成员做初始化,不做赋值外的事情。106构造函数的几种形式构造函数和析构函数构造函数可为内联函数,可以为无参数或带参数,还可以缺省参数,例如:无参的构造函数参数化的构造函数缺省参数的构造函数多构造函数拷贝构造函数107classCArea{public:CArea(){x=0;y=0;}//无参数的构造函数

CArea(intrx,intry=0);//带缺省参数的构造函数

CArea(floatrr){rr=0;}//带一个参数的构造函数

CArea(floatrr,char*ra);//带两个参数的构造函数

};例2.5.1在类中定义构造函数,见p33在类中声明的多种构造函数:例108构造函数举例classClock{public:

Clock(intNewH,intNewM,intNewS);//构造函数

voidSetTime(intNewH,intNewM,intNewS); voidShowTime();private: intHour,Minute,Second;};构造函数和析构函数构造函数的实现:Clock::Clock(intNewH,intNewM,intNewS){ Hour=NewH; Minute=NewM; Second=NewS;}建立对象时构造函数的作用:voidmain(){

Clockc(0,0,0);//隐含调用构造函数,将初始值作为实参。

c.ShowTime();}31110

构造函数的使用何时调用构造函数?在类对象进入其作用域时调用构造函数;构造函数没有返回值,不要在定义构造函数时声明类型;构造函数不需要用户调用,也不能被用户调用;在构造函数体中,不仅可以对数据成员赋初值,还可以包含其他语句如cout;如果用户没有定义构造函数,C++系统会自动生成一个构造函数,其函数体为空,不执行初始化操作;要想初始化一个对象,需要重新定义构造函数,否则对象的状态将是随机的。构造函数和析构函数111带参数的构造函数定义对象时,通过不同参数值的传递实现不同对象的不同初始化,可以使用带参数的构造函数;一般格式:构造函数名(类型1形参1,类型2形参2,……)用户不能显式地调用构造函数,也无法采用常规的调用函数的方法给出实参(如fun(a,b))。实参只能在定义对象时给出,定义对象的一般格式:类名对象名(实参1,实参2,……);例2.5.2:有两个长方体,长、宽、高分别为:(1)12,25,30;(2)15,30,21。用带参数的构造函数编写程序求其体积。构造函数和析构函数112使用参数初始化表

在定义对象时,可使用参数初始化表来实现对数据成员的初始化,在函数首部实现:Box::Box(inth,intw,intlen):height(h),width(w),length(len){}

其含义是:用形参h的值初始化数据成员height,用形参w的值初始化数据成员width,用形参len的值初始化数据成员length。构造函数和析构函数113构造函数的重载

一个类可以定义多个构造函数,使用户选用不同的方式完成对象数据的初始化;重载的所有构造函数同名,但其参数类型、参数个数必须有所区别。构造函数和析构函数114例2.5.3

在例2.5.2的基础上定义两个构造函数,其中一个无参数,一个有参数,见p37

重载构造函数的例子例,可以在类中声明的多种构造函数:classCArea{public:CArea(){x=0;y=0;}//无参数的构造函数

CArea(intrx,intry=0);//带缺省参数的构造函数

CArea(floatrr){rr=0;}//带一个参数的构造函数

CArea(floatrr,char*ra);//带两个参数的构造函数

};115

缺省构造函数在调用构造函数时不必给出实参的构造函数,称为默认构造函数(DefaultConstructor)或缺省构造函数;无参数的构造函数属于缺省构造函数;如果在定义对象时选用无参构造函数,应按以下形式定义对象:

Boxbox1;//调用无参构造函数建立对象在一个类中可以包含多个构造函数,但是在创建每个对象时,只执行其中一个匹配版本的构造函数。构造函数和析构函数116默认形参值的作用函数在声明时可以预先给出默认的形参值,调用时如给出实参,则采用实参值,否则采用预先给出的默认形参值。例如:intadd(intx=5,inty=6){returnx+y;}voidmain(void){add(10,20);//10+20add(10);//10+6add();//5+6}117默认形参值的说明次序默认形参值必须从右向左顺序声明,并且在默认形参值的右面不能有非默认形参值的参数。因为调用时实参取代形参是从左向右的顺序。例:intadd(intx,inty=5,intz=6);//正确intadd(intx=1,inty=5,intz);//错误intadd(intx=1,inty,intz=6);//错误118默认形参值与函数的调用位置调用出现在函数体实现之前时,默认形参值必须在函数原形中给出;而当调用出现在函数体实现之后时,默认形参值需在函数实现时给出。例:intadd(intx=5,inty=6);voidmain(void){add();

//调用在实现前}intadd(intx,inty){returnx+y;}intadd(intx=5,inty=6){returnx+y;}voidmain(void){add();

//调用在实现后}119默认形参值的作用域在相同的作用域内,默认形参值的说明应保持唯一,但如果在不同的作用域内,允许说明不同的默认形参。例:intadd(intx=1,inty=2);voidmain(void){intadd(intx=3,inty=4);add();//使用局部默认形参值(实现3+4)}voidfun(void){...

add();//使用全局默认形参值(实现1+2)}120

使用默认参数的构造函数使用默认参数的构造函数,即构造函数的参数表中没有参数;如果创建对象时不需要通过传递参数来初始化对象,可以使用它。例2.5.4:将例2.5.3的构造函数改为含默认值参数值,长、宽、高的默认值均为10。构造函数和析构函数121

使用默认参数的构造函数注意:在构造函数中使用默认参数提供了建立对象的多种初始化方法,相当于多个重载的构造函数;应该在声明构造函数时指定参数的默认值,而不能只在定义构造函数时指定默认值;如果构造函数的全部参数都指定了默认值,则在定义对象时可以不给出,或给出一个、几个实参;在一个类中定义了全部默认参数的构造函数后,不能再定义重载构造函数,否则会出现二义性。Box(int=10,int=10,int=10);//指定全部参数为默认参数Box();

//声明无参构造函数,是重载构造函数Box(int,int);

//声明有2个参数的构造函数若有以下语句:Boxbox1;

//无法确定调用第1个、还是第2个构造函数?Boxbox2(15,30);

//无法确定调用第1个、还是第3个构造函数?例2.5.5。

析构函数(destructor)也是一个特殊的成员函数,它的作用与构造函数相反.它的名字是类名的前面加一个“~”符号。在C++中“~”是位取反运算符,从这点也可以想到:析构函数是与构造函数作用相反的函数。当对象的生命期结束时,会自动执行析构函数。比如说如果在一个函数中定义了一个对象(它是自动局部对象),当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。析构函数析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作。最典型的情况是构造对象时,在构造函数中分配了资源,例如动态申请了一些内存单元,在对象消失时就要释放这些内存单元。一般由一系列的delete组成。析构函数不返回任何值,没有函数类型,也没有函数参数。因此它不能被重载。一个类可以有多个构造函数,但只能有一个析构函数。析构函数一般情况下,类的设计者应当在声明类的同时定义析构函数,以指定如何完成“清理”的工作。如果用户没有定义析构函数,C++编译系统会自动生成一个析构函数,但它只是徒有析构函数的名称和形式,实际上什么操作都不进行。想让析构函数完成任何工作,都必须在定义的析构函数中指定。析构函数125析构函数析构函数的作用与构造函数相反。析构函数的特点:(1)析构函数不能接受任何参数,也没有返回类型说明;(2)一个类只有一个析构函数,如果用户未编写析构函数,编译系统会自动生成一个缺省的析构函数,此函数不做任何事情;(3)析构函数不能重载。构造函数和析构函数126何时执行析构函数?(1)一个函数中定义的对象(自动局部的),在这个函数结束调用时,对象应该释放,并在对象释放前自动执行析构函数;(2)static类型的局部对象在函数调用结束时并不释放,也不调用析构函数,只在main函数结束或调用exit函数结束程序时,才调用static局部对象的析构函数;(3)一个全局对象,在程序离开其作用域时(如main函数结束或调用exit函数),调用该全局对象的析构函数;(4)用new运算符动态建立的对象,当用delete运算符释放该对象时,先调用该对象的析构函数;(5)如果用户没定义析构函数,C++编译系统会自动生成一个析构函数,只是它实际上不执行任何操作。127构造函数和析构函数共同点都没有返回值,无需指出返回类型;不能被继承;构造函数可以有缺省参数;不能用常规调用方法调用构造函数;当使用完全的限定名(包括对象名、类名、函数名)时可以调用析构函数;定义对象时,编译程序自动调用构造函数,删除对象时,编译程序自动调用析构函数。构造函数和析构函数128

构造函数和析构函数的调用顺序调用析构函数的次序正好与调用构造函数的次序相反,最先被调用的构造函数,其对应的(同一对象中的)析构函数最后被调用,而最后被调用的构造函数,其对应的析构函数最先被调用;“先构造的后析构、后构造的先析构”的先进后出的特征。构造函数和析构函数129图2.7.1构造函数与析构函数调用顺序调用构造函数和析构函数的顺序构造函数和析构函数

例2.6.1

包含构造函数和析构函数的C++程序。130

指向对象的指针对象存储空间的起始地址叫对象指针;可以定义一个指针变量,存放对象空间的起始地址,如:

Timet1;//创建Time类对象t1Time*pt=&t1;//定义指向Time类对象

//的指针pt,并将t1的起始地址赋给pt对象指针131

指向对象的指针可以通过对象指针访问对象和对象的成员,如:

*pt//pt所指向对象t1

(*pt).hour//pt所指向对象中的hour成员,即t1.hourpt->hour//pt所指向对象中的hour成员,即t1.hour

(*pt).get_time()//调用pt所指向的对象中的get_time函数,即t1.get_time

pt->get_time()//调用pt所指向的对象中的

get_time函数,即t1.get_time对象指针132

指向对象成员的指针指向对象数据成员的指针:数据类型名*指针变量名;

p1=&t1.hour;

//将对象t1的数据成员hour的地址赋给p1,p1指向t1.hourcout<<*p1<<endl;

//输出t1.hour的值对象指针133

指向对象成员的指针指向对象成员函数的指针:1、普通函数:数据类型名(*指针变量名)(参数表列);

2、指向公用成员函数的指针变量:数据类型名(类::*指针变量名)(参数表列);3、使指针变量指向一个公用成员函数的一般形式为:

指针变量名=&类名::成员函数名;例2.8.1。对象指针每个对象中的数据成员都分别占有存储空间,如果对同一个类定义了n个对象,则有n组同样大小的空间以存放n个对象中的数据成员。但是,不同对象都调用同一个函数代码段。当不同对象的成员函数引用数据成员时,怎么能保证引用的是所指定的对象的数据成员呢?假如,对于定义的Box类,定义了3个同类对象a,b,c。如果有a.volume(),应该是引用对象a中的height,width和length,计算出长方体a的体积。如果有b.volume(),应该是引用对象b中的height,width和length,计算出长方体b的体积。而现在都用同一个函数段,系统怎样使它分别引用a或b中的数据成员呢?this指针在每一个成员函数中都包含一个特殊的指针,这个指针的名字是固定的,称为this。它是指向本类对象的指针,它的值是当前被调用的

温馨提示

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

最新文档

评论

0/150

提交评论