C程序设计语言(李雁妮)第9章.ppt

上传人:b****1 文档编号:1759522 上传时间:2022-10-23 格式:PPT 页数:128 大小:1.19MB
下载 相关 举报
C程序设计语言(李雁妮)第9章.ppt_第1页
第1页 / 共128页
C程序设计语言(李雁妮)第9章.ppt_第2页
第2页 / 共128页
C程序设计语言(李雁妮)第9章.ppt_第3页
第3页 / 共128页
C程序设计语言(李雁妮)第9章.ppt_第4页
第4页 / 共128页
C程序设计语言(李雁妮)第9章.ppt_第5页
第5页 / 共128页
点击查看更多>>
下载资源
资源描述

C程序设计语言(李雁妮)第9章.ppt

《C程序设计语言(李雁妮)第9章.ppt》由会员分享,可在线阅读,更多相关《C程序设计语言(李雁妮)第9章.ppt(128页珍藏版)》请在冰豆网上搜索。

C程序设计语言(李雁妮)第9章.ppt

第9章类与对象,9.1类的基本概念9.2类中成员9.3定义有效、高质量的类9.4对象9.5综合示例小结练习题,本章要点:

类的基本概念;类成员;如何有效地定义类;对象及其类别;对象的生成与消除。

9.1类的基本概念在第3章,我们已经阐述了类型的基本概念,即一个类型是一个值集,以及定义在这个值集上的操作值。

就类型的内涵而言,一个类型是应用领域中一个概念的具体表示或抽象。

例如,C+语言中的基本类型bool、int、float、char就是关于布尔量、数值、字符处理等概念的具体表示或抽象。

一般而言,每一种语言都内置了一些基本类型,C+语言亦不例外。

按照面向对象(Object-Oriented,OO)的理念,在应用中,当某一个概念和基本类型没有直接对应关系(即无法用基本类型直接表示)时,我们就应当自定义一种新的类型以直接表达这一概念。

问题是:

为什么要定义成类型而不是其它形式的程序实体(如数据结构或函数)呢?

理由如下:

类型比其它程序实体的表示更易于理解;类型比其它程序实体的表示更易于修改;类型可使得程序更为简明、清晰;类型亦使得对代码的多样化分析切实可行,特别是使得编译程序能够根据类型定义与使用之间的关系,检查出程序中的非法使用,而不是直到测试时才能发现。

那么,我们又应如何定义这种自定义类型,以确切地表达应用领域中的概念呢?

首先让我们考察一下C+语言基本类型的定义和使用方式,从中得到启示。

对于C+程序员而言,使用一个基本类型时看到的只是类型名和一组操作的声明(包括操作名、参数、操作涵义、操作使用规则),而看不到操作的具体实现,也看不到该类型所定义的内部数据结构。

例如,对基本类型double,程序员看到并能够使用的操作是+、*、/、=、=、+=、=、*=、/=、()?

:

、sizeof,等等,这些操作都只给出了声明而没有给出具体的实现。

程序员看不到(也没有必要看到)double类型对象的数据结构。

因此,按照面向对象的理念,我们所定义的自定义类型,对于C+程序员而言,使用它时应当与使用一个基本类型没有本质区别,看到的也应当只是类型名和一组操作的声明(包括操作名、参数、操作涵义、操作使用规则),而不应看到操作的具体实现及该类型所定义的内部数据结构。

我们将具有和语言基本类型特征一样的自定义类型称为类(Class)。

一个C+类是一个用户自定义(User-defined)类型。

构造这种自定义数据类型的基本思想就是要将所自定义类型的界面与其实现进行分离。

C+的类型定义机制(及相关的一些机制)渗透与贯穿了面向对象的思想,它使得其表现和基本类型十分相似,两者只是在对象生成上有所差异。

9.2类中成员9.2.1类中的成员如何定义一个自定义类型呢?

我们利用已有的手段(C语言的结构struct)来定义或构造一个用户自定义类型。

例如,应用中有一个日期(Date)概念,利用struct类型定义方法,我们可定义日期类型如下:

structDateintyear;/year的值集是1700,MAXINTintmonth;/month的值集是1,12intday;/day的值集是1,31;,和基本类型相比,这种类型定义方法存在两大问题:

(1)它仅描述了该类实体所具有的特征(年、月、日),由此可推断出该类型的值集(成员值集的笛卡尔积),即只定义了该类型的值集,而没有定义该类型量所能进行的操作集。

假定与该自定义类型Date相关的操作有:

voidinit_date(Date/增加日,但C的结构定义方法却没有一种明显的绑定机制说明或强制这些操作是Date的操作集。

(2)由于该日期类型的成员在类型外可见,故程序员可在类外任意添加对该类型量的操作,因此,该类型的操作集是开放的,并且不封闭于其值集。

假定joking是Date类型的一种操作,示例代码如下:

voidjoking(Date/语法正确,当m1时语义错误voidf(),Datetoday;init_Date(today,12,12,2001);/.joking(today);/.add_day(today,1);由于joking函数中的d.m*=20;等价于d.m=d.m*20;当m1时,则操作结果将超出month的值集。

因此,用C的结构定义方式所定义的自定义类型是不完备的,所定义的类型自然亦不能称其为真正的类型。

解决上述问题的有效做法是像基本类型一样将其数据成员(DataMember,struct中定义的变量)和其上的操作(struct中的函数,亦称为成员函数/方法,MemberFunction/Method)显式地定义(绑定)在一起(C+对C的结构定义进行了扩充,允许将成员函数定义在结构体中),以显式地定义其值集和操作集,但前提是没有其它函数有权直接访问这个类型的任何数据成员,即:

这样,我们从形式上就定义了一个具有类型特征(一个值集和封闭于该值集上的操作集)的自定义类型类。

从上述示例中已经看到,一个类的成员分为两大类:

数据成员和成员函数。

数据成员即类中各变量/对象的定义部分,它们描述了该类对象所具有的属性,各数据成员值集的笛卡尔积构成了该类型的值集。

成员函数即类中所有函数的集合,它描述了对该类对象所能进行的操作,即它们构成了该类的操作集。

C+的结构类型是一种特殊的类,其成员的访问控制默认为公有(public),即成员在类外可见。

9.2.2类的访问控制前面已经谈到:

一个自定义类型的表现形式和使用方法应与基本类型一致,即类应遵循信息隐藏原则,它应为一个封装体,对外只暴露接口(操作的接口),而隐藏其实现(数据成员及操作的实现)。

但我们在9.2.1节中用struct定义的Date类型,由于其数据成员在类外可见,因此,程序员就还有可能写出如下的代码:

voidjoking(Date/语法正确,当m1时语义错误,亦即:

即使我们在自定义类型时显式地定义了该类型的值集(数据成员值集的笛卡尔积)和操作集(成员函数的集合),但由于没有对数据成员施加访问控制,用户就有可能在自定义类外无限制地扩张其操作集。

因此,用struct定义的、没有对其成员施加任何访问控制的自定义类型不是一个真正意义上的类。

正确的做法是:

在自定义类型时对其成员施加有效的访问控制,使其具有类型的真正内涵。

对类的成员施加访问控制主要有以下四个目的:

(1)使得这个类的成员函数(public函数)确实构成了这个类的操作集。

(2)实施访问控制易于进行出错定位(这是调试程序时首先要解决、也是最难解决的一个问题)。

(3)用成员函数的声明组成这个类的接口,将它的数据成员和成员函数的实现封装起来,以减少程序的修改对外部的影响。

(4)有利于掌握一个类型的使用方式,使得类的用户对类的使用只需了解接口(了解数据结构后才能使用类型,实际上是不得已而为之)。

C+用三种访问控制符来限定对类成员的访问,它们是:

private(私有),具有该访问控制的成员的作用域在类内;,public(公有),具有该访问控制的成员的作用域在类内和类外;protectd(保护),具有该访问控制的成员的作用域在类内和相应的子类中。

现在我们采用C+的类定义方式来重新定义类Date:

/假定类的界面定义存放在Date.h头文件中classDate/类的界面定义,注意:

在类Date的定义中,class关键字代替了struct。

用class定义的类,类中第一部分默认为private访问权限(也可显式地添加private访问控制符,struct默认的访问权限是public)。

各访问控制权限的作用域直至遇到下一个访问控制符为止。

在以上类Date的(界面)定义中,由于其数据成员的访问控制权限为private,因此,这些数据成员对外隐藏;因成员函数的访问控制权限为public,所以它对外暴露,即在类Date的接口/界面定义中,隐藏了数据结构及成员函数的实现,只暴露了类的操作接口(成员函数接口)。

因此,非成员函数joking对类Date私有数据成员的访问将被禁止:

voidjoking(Date/编译错误!

m为类的私有成员,类外不能访问,为了实现成员函数接口与实现的分离,C+允许采用类名约束机制(类名:

),将成员函数的实现(定义)写在类界面定义以外(当然,成员函数的实现亦可写在类内,此时,该成员函数为内联函数,但这种类的定义风格没有遵循接口与实现的分离原则,因此不提倡)。

类Date各成员函数的实现如下:

#include“Date.h”/包含Date类界面定义voidDate:

init_Date(intdd,intmm,intyy)d=dd;m=mm;y=yy;,在上述Date类的定义中,其类成员的访问控制程度不同,也称其为“能见度”不同。

在自定义一个C+类时,应对其成员施加有效的访问控制。

一般而言,应将类的数据成员(和其它的支撑操作)的访问权限设置为private,而将类对外提供的操作接口设置为public。

语法上,用访问控制符加“:

”对类成员进行访问控制约束。

在一个类的定义中,访问控制符的个数、次序不限,各访问控制符亦无优先级。

例如,以下两个类的接口定义是等价的:

但第二种Person的定义风格不可取。

它将类的private段和public段混杂在一起,使得类的界面变得模糊不清。

我们提倡的风格是:

要么将类的private段放在开始,要么为了更加突出类对外提供的接口而将private段放在类的最后。

因此,类Person还可以下述风格进行定义:

9.2.3类的构造函数1构造函数对于9.2.2节所定义的类Date,我们可通过调用成员函数init_Date对类对象进行初始化,但这样做既不优美又很容易出错。

因为在程序中没有任何一个地方提示(或采用某种机制强制)一个类的对象在使用前必须初始化。

如果程序员生成一个类的对象后,在使用前忘记了对其进行初始化(或对其初始化了两次),则可能造成灾难性的后果。

例如,下述代码:

由于未初始化的today对象中的数据成员是随机数,所以后续的基于today对象的操作将全部发生错误。

程序设计的悲哀往往就是对不确定的(对象/变量)状态,程序员误认为其确定而做了确定的操作。

避免上述问题发生的一个最有效的办法是:

在定义类时允许程序员声明一个成员函数,该成员函数的目的就是初始化类的对象,该函数在类的对象生成时由系统自动调用。

由于所声明的函数用于构造一个给定类对象的值,因此,称其为构造函数(Constructor)。

类的构造函数名与类名完全相同(注意:

C+是大小写敏感的),并且其构造函数无返回类型。

一个构造函数的任务就是类实例/对象(Instance/Object)的生成与初始化。

C+语法规定:

在类对象的生成时,其构造函数由系统自动调用,程序员只需根据构造函数接口提供相应的实参(对象初始化值)即可。

因此,在自定义类型时,我们应提供构造函数,以确保类对象的生成和正确初始化。

故类Date可改进为,利用类提供的构造函数Constructor,我们可采用如下方式生成类Date的对象并对其初始化:

/显式调用Constructor生成一临时对象,并将其值赋给生成的对象todayDatetoday=Date(12,12,2001);/this_day对象名后直接跟对应Constructor的实参表Datethis_day(12,12,2001);而以下的对象生成及初始化方式是错误的:

/my_birthday对象未给出(调用构造函数)实参表Datemy_birthday;/error_date对象给出的实参表与构造函数的接口不吻合Dateerror_date(12,12);,2构造函数的重载在面向对象程序设计中,类是程序的基本构件。

由于类的用户不同,导致其对对象的生成方式及初始化方式的需求可能不同。

因此,应用中所定义的实例类(ConcretClass可以生成对象的类)应向用户提供多个重载的构造函数,以满足用户不同的需求。

由于Date类是一个实例类,因此,Date类对外应提供一组重载的构造函数:

/Date.hclassDateintd,m,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 初中教育

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1