第7单元类和对象(I) 第7单元类和对象(I) 本单元教学目标 介绍面向对象程序设计方法的基本原理以及类和对象的概念。 教学要求 掌握面向对象的程序设计思想,类和对象的概念,以及类的声明方法和对象的引用。 授课內容 71面向对象的程序设计 在面向对象的程序设计技术( OOP: Object Oriented Programming)出现前,程序员们一 般采用面向过程的程序设计方法。面向过程的程序设计方法采用函数(或过程)来描述对数 据结构的操作,但又将函数与其所操作的数据分离开来。作为对现实世界的抽象,函数和它 所操作的数据是密切相关、相互依赖的:特定的函数往往要对特定的数据结构进行操作;如 果数据结构发生改变,则必须改写相应的函数。这种实质上的依赖与形式上的分离使得用面 向过程的程序设计方法编写出来的大程序不但难于编写,而且难于调试和修改 面向对象程序设计从所处理的数据入手,以数据为中心而不是以功能为中心来描述系 统。数据相对于功能而言具有更强的稳定性。面向对象程序设计与结构化程序设计最大的区 别就在于,前者首先关心的是所要处理的数据,而后者首先关心的是功能。 面向对象程序设计是一种围绕真实世界的概念来组织模型的程序设计方法,它采用对象 来描述问题空间中的实体。关于对象这一概念,目前还没有统一的定义。一般的认为,对象 是包含现实世界物体特征的抽象实体,反映了系统为之保存信息和(或)与之交互的能力。 对象是一些属性及服务的封装体,在程序设计领域,可以用“对象=数据+作用于这些数据 上的操作”这一公式来表达 类是具有相同操作功能和相同的数据格式(属性)的对象的集合,可以看作抽象数据类 型的具体实现。从外部看,类的行为可以用新定义的操作(方法)加以规定。类是对象集合 的抽象,规定了这些对象的公共属性和方法;对象是类的一个实例。例如,苹果是一个类, 而放在桌上的那个苹果则是一个对象。对象和类的关系相当于一般的程序设计语言中变量和 变量类型的关系 消息是向某对象请求服务的一种表达方式。对象内有方法和数据,外部的用户或对象对 该对象提出的服务请求,可以称为向该对象发送消息。 面向对象的编程方法具有四个基本特征:
第 7 单元 类和对象(I) - 131 - 第 7 单元 类和对象(I) 本单元教学目标 介绍面向对象程序设计方法的基本原理以及类和对象的概念。 教学要求 掌握面向对象的程序设计思想,类和对象的概念,以及类的声明方法和对象的引用。 授课内容 7.1 面向对象的程序设计 在面向对象的程序设计技术(OOP: Object Oriented Programming)出现前,程序员们一 般采用面向过程的程序设计方法。面向过程的程序设计方法采用函数(或过程)来描述对数 据结构的操作,但又将函数与其所操作的数据分离开来。作为对现实世界的抽象,函数和它 所操作的数据是密切相关、相互依赖的:特定的函数往往要对特定的数据结构进行操作;如 果数据结构发生改变,则必须改写相应的函数。这种实质上的依赖与形式上的分离使得用面 向过程的程序设计方法编写出来的大程序不但难于编写, 而且难于调试和修改。 面向对象程序设计从所处理的数据入手,以数据为中心而不是以功能为中心来描述系 统。数据相对于功能而言具有更强的稳定性。面向对象程序设计与结构化程序设计最大的区 别就在于,前者首先关心的是所要处理的数据,而后者首先关心的是功能。 面向对象程序设计是一种围绕真实世界的概念来组织模型的程序设计方法,它采用对象 来描述问题空间中的实体。关于对象这一概念,目前还没有统一的定义。一般的认为,对象 是包含现实世界物体特征的抽象实体,反映了系统为之保存信息和(或)与之交互的能力。 对象是一些属性及服务的封装体,在程序设计领域,可以用“对象=数据+作用于这些数据 上的操作”这一公式来表达。 类是具有相同操作功能和相同的数据格式(属性)的对象的集合,可以看作抽象数据类 型的具体实现。从外部看,类的行为可以用新定义的操作(方法)加以规定。类是对象集合 的抽象,规定了这些对象的公共属性和方法;对象是类的一个实例。例如,苹果是一个类, 而放在桌上的那个苹果则是一个对象。对象和类的关系相当于一般的程序设计语言中变量和 变量类型的关系。 消息是向某对象请求服务的一种表达方式。对象内有方法和数据,外部的用户或对象对 该对象提出的服务请求,可以称为向该对象发送消息。 面向对象的编程方法具有四个基本特征:
第7单元类和对象(I) 1.抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与 当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,忽略与主题 无关的细节。例如,在设计一个学生成绩管理系统的过程中,考察学生张三这个对象时,我 们只关心他的班级、学号、成绩等,而他的身高、体重等信息就可以忽略。抽象包括两个方 面,一是过程抽象,二是数据抽象。过程抽象是指任何一个明确定义功能的操作都可被使用 者看作单个的实体看待,尽管这个操作实际上可能由一系列更低级的操作来完成。数据抽象 定义了数据类型和施加于该类型对象上的操作,并限定了对象的值只能通过使用这些操作修 改和观察。 2.封装:封装是面向对象的特征之一,是对象和类概念的主要特性。封装把过程和数 据封藏起来,对数据的访问只能通过已定义的界面。面向对象技术的基本概念就是现实世界 可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象 旦定义了一个对象的特性,则有必要决定这些特性的可见性,即哪些特性对外部世界是可 见的,哪些特性用于表示内部状态。通常,应禁止直接访问一个对象的实际表示,只能通过 操作接口访问对象,这称为信息隐藏。事实上,信息隐藏是用户对封装性的认识,封装则为 信息隐藏提供支持。封装保证了模块具有较好的独立性,使得程序维护修改较为容易。对应 用程序的修改仅限于类的内部,因而可以将应用程序修改带来的影响减少到最低限度。 3.继承:继承是一种联结类与类的层次模型。继承允许和鼓励类的重用,提供了一种 明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类 继承了原来类的特性,新类称为原来类的派生类(子类),而原来类称为新类的基类(父类)。 派生类可以从其基类那里继承方法和成员变量,当然也可以对之进行修改或增加新的方法使 之更适合特殊的需要。这也体现了大自然中一般与特殊的关系。继承性很好地解决了软件的 可重用性问题。 4.多态性:多态性是指允许不同类的对象对同一消息作出响应。例如同样的加法,把 两个时间加在一起和把两个整数加在一起的内涵肯定完全不同。多态性包括参数化多态性和 包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用 程序函数同名问题。 面向对象程序设计具有许多优点:开发时间短,效率高,可靠性高,所开发的程序更强 壮。由于面向对象编程的编码可重用性,可以在应用程序中大量采用成熟的类库,从而缩短 了开发时间,使应用程序更易于维护、更新和升级。继承和封装使得应用程序的修改带来的 影响更加局部化。 72类与对象 721类的声明 C++类的结构比较复杂,可以将其看成是一种既包含数据又包含函数的数据类型。显然, 描述不同编程对象的类应有不同的内部结构,所以在使用类来声明对象前应先说明其结构 声明一个类的一般形式为: lass 成员表>; public
第 7 单元 类和对象(I) - 132 - 1. 抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与 当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,忽略与主题 无关的细节。例如,在设计一个学生成绩管理系统的过程中,考察学生张三这个对象时,我 们只关心他的班级、学号、成绩等,而他的身高、体重等信息就可以忽略。抽象包括两个方 面,一是过程抽象,二是数据抽象。过程抽象是指任何一个明确定义功能的操作都可被使用 者看作单个的实体看待,尽管这个操作实际上可能由一系列更低级的操作来完成。数据抽象 定义了数据类型和施加于该类型对象上的操作,并限定了对象的值只能通过使用这些操作修 改和观察。 2. 封装:封装是面向对象的特征之一,是对象和类概念的主要特性。封装把过程和数 据封藏起来,对数据的访问只能通过已定义的界面。面向对象技术的基本概念就是现实世界 可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 一旦定义了一个对象的特性,则有必要决定这些特性的可见性,即哪些特性对外部世界是可 见的,哪些特性用于表示内部状态。通常,应禁止直接访问一个对象的实际表示,只能通过 操作接口访问对象,这称为信息隐藏。事实上,信息隐藏是用户对封装性的认识,封装则为 信息隐藏提供支持。封装保证了模块具有较好的独立性,使得程序维护修改较为容易。对应 用程序的修改仅限于类的内部,因而可以将应用程序修改带来的影响减少到最低限度。 3. 继承:继承是一种联结类与类的层次模型。继承允许和鼓励类的重用,提供了一种 明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类 继承了原来类的特性,新类称为原来类的派生类(子类),而原来类称为新类的基类(父类)。 派生类可以从其基类那里继承方法和成员变量,当然也可以对之进行修改或增加新的方法使 之更适合特殊的需要。这也体现了大自然中一般与特殊的关系。继承性很好地解决了软件的 可重用性问题。 4. 多态性:多态性是指允许不同类的对象对同一消息作出响应。例如同样的加法,把 两个时间加在一起和把两个整数加在一起的内涵肯定完全不同。多态性包括参数化多态性和 包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用 程序函数同名问题。 面向对象程序设计具有许多优点:开发时间短,效率高,可靠性高,所开发的程序更强 壮。由于面向对象编程的编码可重用性,可以在应用程序中大量采用成熟的类库,从而缩短 了开发时间,使应用程序更易于维护、更新和升级。继承和封装使得应用程序的修改带来的 影响更加局部化。 7.2 类与对象 7.2.1 类的声明 C++类的结构比较复杂,可以将其看成是一种既包含数据又包含函数的数据类型。显然, 描述不同编程对象的类应有不同的内部结构,所以在使用类来声明对象前应先说明其结构。 声明一个类的一般形式为: class { private: ; public:
第7单元类和对象(I) 成员表 } 其中关键字clas指出下面要声明的是一个类,是程序员为所声明的类起的名字 由一个个该类成员的声明组成,这些成员可以是变量(数据成员),也可以是函数 (成员函数);关键字 private引出的成员叫做私有成员,对私有成员的访问权限制在该类的 内部,即只允许该类中的成员函数访问。由于类成员被默认为私有的,所以该关键字可以省 略;关键字 public引出的成员叫做公有成员,公有成员则允许该类以外的函数访问;而关键 字 protected引出的成员叫做保护成员,保护成员的访问权限将在第8单元介绍 例7-声明一个 Person类,用来说明人类对象 说明:将 Person类的声明放在头文件中。为项目添加头文件的方法与添加源代码 文件的方法类似,建立项目后,使用 Developer Studio的菜单选项Fle/New.,在File选项 卡中选择CC++ Header file项,并在对话框右面的Fle框中填写文件名(可与项目名相同), 然后按OK键即可生成一空白头文件。然后输入如下代码 程序 / Example7-1( Person.h):声明 Person类 /类 Person的声明 class person private char m strName [20] nt publ void Register(char *Name, int Age, char Sex) void Get Name(char *Name) GetAgeo Get Sex 作为一个人,可能有许多特征:姓名、性别、年龄、身高、体重、民族、学历、职业, 类 Person体现了对人的抽象,它集中了人所具有的共性。同时,在该类中还说明了对一个抽象 人的属性进行操作的方法:登录一个人的信息的函数 Register();获取一个人的信息的函数 GetName()、 GetAge()和 GetSex()。该例中,将数据成员声明为私有的以阻止外界对它们 的随意访问,而成员函数则声明为公有的,它们便是外界访问类中数据成员的统一接口。本例 中的 private关键字可以省略。另外,在类的声明中,不同访问权限的成员的书写顺序可以任意 排列。在这种情况下,要注意使用关键字 private,不能随便省略。 在 Visual c++程序中,在声明类的数据成员时,通常在其名前加上前缀“m”,以区别于 普通的变量名。 在声明一个类时应注意:
第 7 单元 类和对象(I) - 133 - ; protected: ; … … }; 其中关键字 c1ass 指出下面要声明的是一个类,是程序员为所声明的类起的名字; 由一个个该类成员的声明组成,这些成员可以是变量(数据成员),也可以是函数 (成员函数);关键字 private 引出的成员叫做私有成员,对私有成员的访问权限制在该类的 内部,即只允许该类中的成员函数访问。由于类成员被默认为私有的,所以该关键字可以省 略;关键字 public 引出的成员叫做公有成员,公有成员则允许该类以外的函数访问;而关键 字 protected 引出的成员叫做保护成员,保护成员的访问权限将在第 8 单元介绍。 [例 7-1] 声明一个 Person 类,用来说明人类对象。 说 明:将 Person 类的声明放在头文件中。为项目添加头文件的方法与添加源代码 文件的方法类似,建立项目后,使用 Developer Studio 的菜单选项 File/New…,在 File 选项 卡中选择 C/C++ Header File 项,并在对话框右面的 File 框中填写文件名(可与项目名相同), 然后按 OK 键即可生成一空白头文件。然后输入如下代码。 程 序: // Example 7-1(Person.h):声明 Person 类 // 类 Person 的声明 class Person { private: char m_strName[20]; int m_nAge; int m_nSex; public: void Register(char *Name, int Age, char Sex); void GetName(char *Name); int GetAge(); char GetSex(); }; 作为一个人,可能有许多特征:姓名、性别、年龄、身高、体重、民族、学历、职业…, 类 Person 体现了对人的抽象,它集中了人所具有的共性。同时,在该类中还说明了对一个抽象 人的属性进行操作的方法:登录一个人的信息的函数 Register();获取一个人的信息的函数 GetName()、GetAge()和 GetSex()。该例中,将数据成员声明为私有的以阻止外界对它们 的随意访问,而成员函数则声明为公有的,它们便是外界访问类中数据成员的统一接口。本例 中的 private 关键字可以省略。另外,在类的声明中,不同访问权限的成员的书写顺序可以任意 排列。在这种情况下,要注意使用关键字 private,不能随便省略。 在 Visual C++程序中,在声明类的数据成员时,通常在其名前加上前缀“m_”,以区别于 普通的变量名。 在声明一个类时应注意:
第7单元类和对象(I) 类中任何成员都不得用关键字 extern,auto和 register进行修饰 2.不得在类声明中对数据成员使用表达式进行初始化。 72.2成员函数的定义 从例7-1中可以看出,在类的声明中仅给出了成员函数的原型,函数的定义还需在其他地 方(通常每个类的成员函数的定义放在一起构成一个源程序文件)给出。类的成员函数的一般 形式为 类型〉() 函数体 其中作用域运算符“”指出成员函数的类属。没有类属的函数称为公共函数,在前面各单元中 用到的函数均为公共函数。 例7-2| Person类成员函数的声明 说明:按例7-1说明的方法添加头文件,输入 Person类的声明。然后为项目添加一源 代码文件,输入以下程序 程序 / Example7-2( Person.cpp): Person类成员函数的定义 #include void Person:: Register(const char *name, int age, char sex) strcpy (m s strName, name m sex =(sex =m3?0:1); void Person:: Get Name(char *name strcpy (name, m strName) int Person: GetAgeo return m nAge char Person:: GetSexo return (m nSex==0?'m:'f") 类中的成员函数有时很简单,这样的函数最适合写成内联成员函数以提高程序的执行效率
第 7 单元 类和对象(I) - 134 - 1.类中任何成员都不得用关键字 extern,auto 和 register 进行修饰。 2.不得在类声明中对数据成员使用表达式进行初始化。 7.2.2 成员函数的定义 从例 7-1 中可以看出,在类的声明中仅给出了成员函数的原型,函数的定义还需在其他地 方(通常每个类的成员函数的定义放在一起构成一个源程序文件)给出。类的成员函数的一般 形式为: :: () { } 其中作用域运算符“::”指出成员函数的类属。没有类属的函数称为公共函数,在前面各单元中 用到的函数均为公共函数。 [例 7-2] Person 类成员函数的声明。 说 明:按例 7-1 说明的方法添加头文件,输入 Person 类的声明。然后为项目添加一源 代码文件,输入以下程序。 程 序: // Example 7-2(Person.cpp):Person 类成员函数的定义 #include #include “person.h” void Person:: Register(const char *name, int age, char sex) { strcpy(m_strName,name); m_nAge = age; m_nSex = (sex = = ‘m’?0:1); } void Person::GetName(char *name) { strcpy(name, m_strName); } int Person:: GetAge() { return m_nAge; } char Person:: GetSex() { return (m_nSex = = 0?’m’:’f’); } 类中的成员函数有时很简单,这样的函数最适合写成内联成员函数以提高程序的执行效率
第7单元类和对象(I) 135 对于成员函数来说,除了可以采用关键词 inline将其声明为内联函数外,还有一种更简单的方 法,就是在类的声明中直接定义成员函数的函数体,这样的函数自动成为内联成员函数。例如, 可将类 Person的成员函数声明为内联成员函数: class person char m strName [20] t int mm sex public void ister(const char sname int age, char sex) i strcpy(m strName, name) m nAge age m nSex =(sex =='m?0: 1) void Get Name(char * name i strcpy (name, m strName) GetAge o I return m nA, char Getsex I return (m nSex ==0?'m:'f") 类的成员函数与普通函数一样,可以重载,也可以带有缺省参数。 723公有成员和私有成员 为了实现既要隐藏数据,又要为外界使用数据提供接口的封装目的,通常是将类中的数据 成员声明为私有的而将成员函数声明为公有的。然而,在设计一个具体类时,各成员的访问权 限还应根据实际需要而定。一般说来,将类中的数据成员声明成公有的将难以保证该成员的安 全性,而将类的中成员函数声明成私有时还是非常必要的。例如,类中的一个只供其成员函数 调用的成员函数就应当声明为私有的 习惯上将具有全局作用域的类声明放在一个头文件中,将成员函数的定义放在一个另一个 源程序文件中,以提高编程的灵活性。 724对象 对象是类的实例。从技术上讲,一个对象就是一个具有某种类类型的变量。与普通变量 样,对象也必须先经声明才可以使用。声明一个对象的一般形式为
第 7 单元 类和对象(I) - 135 - 对于成员函数来说,除了可以采用关键词 inline 将其声明为内联函数外,还有一种更简单的方 法,就是在类的声明中直接定义成员函数的函数体,这样的函数自动成为内联成员函数。例如, 可将类 Person 的成员函数声明为内联成员函数: class Person { private: char m_strName[20]; int m_nAge; int m_nSex; public: void Register(const char *name, int age, char sex) { strcpy(m_strName,name); m_nAge = age; m_nSex = (sex = = ‘m’?0:1); } void GetName(char *name) { strcpy(name, m_strName); } int GetAge() { return m_nAge; } char GetSex() { return (m_nSex = = 0?’m’:’f’); } }; 类的成员函数与普通函数一样,可以重载,也可以带有缺省参数。 7.2.3 公有成员和私有成员 为了实现既要隐藏数据,又要为外界使用数据提供接口的封装目的,通常是将类中的数据 成员声明为私有的而将成员函数声明为公有的。然而,在设计一个具体类时,各成员的访问权 限还应根据实际需要而定。一般说来,将类中的数据成员声明成公有的将难以保证该成员的安 全性,而将类的中成员函数声明成私有时还是非常必要的。例如,类中的一个只供其成员函数 调用的成员函数就应当声明为私有的。 习惯上将具有全局作用域的类声明放在一个头文件中,将成员函数的定义放在一个另一个 源程序文件中,以提高编程的灵活性。 7.2.4 对象 对象是类的实例。从技术上讲,一个对象就是一个具有某种类类型的变量。与普通变量一 样,对象也必须先经声明才可以使用。声明一个对象的一般形式为:
第7单元类和对象(I) 136- ,, 例如语句 Person personl, person2 声明了两个名为 personl和 person2的 Person类的对象。 在程序中使用一个对象,通常是通过对体现对象特征的数据成员的操作实现的。当然,由 于封装性的要求,这些操作又是通过对象的成员函数实现的。具体来说: 1成员函数访问同类中的数据成员,或调用同类中的其他成员函数,可直接使用数据成员 名或成员函数名,可参看例7-2中各成员函数的定义 2.在对象外访问其数据成员或成员函数需使用运算符“.”访问对象的成员,例如 3.直接访问一个对象中的私有成员则属于非法操作,将导致编译错误; 4同类对象之间可以整体赋值。例如 personl person2: 5对象用作函数的参数时属于赋值调用;函数可以返回一个对象 例7-3]人事资料的输入输出 说明:按例7-1说明的方法添加头文件,输入 Person类的声明。然后为项目添加一源 代码文件,输入以下程序。 程序 / Example7-3:人事资料的输入和输出 # include“ person.h” void main o void OutPersonData(Person) char name [20], sex int Person personl, person2 cout >name >> age >> sex personl Register(name, age, sex) person2 Register("Zhang3", 19, 'm,) cout<“ personl:\t; cout<<“ person2:\t” OutPersonData(person2) per
第 7 单元 类和对象(I) - 136 - , , … 例如语句 Person person1, person2; 声明了两个名为 personl 和 person2 的 Person 类的对象。 在程序中使用一个对象,通常是通过对体现对象特征的数据成员的操作实现的。当然,由 于封装性的要求,这些操作又是通过对象的成员函数实现的。具体来说: 1.成员函数访问同类中的数据成员,或调用同类中的其他成员函数,可直接使用数据成员 名或成员函数名,可参看例 7-2 中各成员函数的定义; 2.在对象外访问其数据成员或成员函数需使用运算符“.”访问对象的成员,例如 nAge = person1.GetAge(); 3.直接访问一个对象中的私有成员则属于非法操作,将导致编译错误; 4.同类对象之间可以整体赋值。例如 person1 = person2; 5.对象用作函数的参数时属于赋值调用;函数可以返回一个对象。 [例 7-3] 人事资料的输入输出。 说 明:按例 7-1 说明的方法添加头文件,输入 Person 类的声明。然后为项目添加一源 代码文件,输入以下程序。 程 序: // Example 7-3:人事资料的输入和输出 #include #include “person.h” void main() { void OutPersonData(Person); char name[20], sex; int age; Person person1, person2; cout > name >> age >> sex; person1.Register(name, age, sex); person2.Register(“Zhang3”, 19, ‘m’); cout << “person1: \t”; OutPersonData(person1); cout << “person2: \t”; OutPersonData(person2); person2 = person1;
第7单元类和对象(I) cout() 即构造函数与类同名,且没有返回值类型。构造函数既可在类外定义,也可作为内联函数在 类内定义,也允许重载。 例7-4为类 Person增加构造函数 程序 ∥/ Example7-4:为类 Person增加构造函数 #include class pe Private char m strName [20] int m nAs m I public person( const char*name, int age, char sex)//构造函数
第 7 单元 类和对象(I) - 137 - cout (); 即构造函数与类同名,且没有返回值类型。构造函数既可在类外定义,也可作为内联函数在 类内定义,也允许重载。 [例 7-4] 为类 Person 增加构造函数。 程 序: // Example 7-4:为类 Person 增加构造函数 #include class Person { Private: char m_strName[20]; int m_nAge; int m_nSex; public: person(const char *name, int age, char sex) //构造函数
第7单元类和对象(I) 138 strcpy(m strName, name) m nAge age m nSex =(sex =='m?0: 1) void Register(char *Name, int Age, char Sex) void Get Name(char *Name) char GetSexo 在创建一个对象时,系统自动调用对象所属类的构造函数。如果定义了带有参数的构造 函数,就可以在声明对象时利用实参对对象进行初始化。例如,当遇到声明 Person personI(“ Zhang3”,19,“f”) 时,编译器就调用构造函数 Person: Person(const char * int, char) 来创建对象 personI并用实参初始化其数据成员。注意,遇到带有关键字 extern修饰的外部 对象声明时不调用构造函数,因为该声明属于对外部已经声明过的对象的引用性声明,这 点和外部变量的声明相同。 对于不需通过参数初始化的成员变量,也可以使用下面的形式进行初始化 ():(),…,() 例如 MyClass: MyClass(: m bAlready(FALSE), m n Count(O) 对象具有变量的性质,一个类的对象也可以作为另一个类的数据成员。对象成员的初始 化也可通过上述方法进行,其实质是对其构造函数的调用 应当说明的是,如果在类中没有定义任何构造函数的话,系统会为自动地为它定义一个 形如 () 的缺省构造函数。这种不带任何参数且函数体为空的构造函数就叫作缺省的构造函数。如果 在类声明中已经声明、定义了构造函数,则系统不再提供缺省的构造函数。 与构造函数相对应,析构函数( Destructor)用于撤消一个对象。析构函数的声明格式
第 7 单元 类和对象(I) - 138 - { strcpy(m_strName, name); m_nAge = age; m_nSex = (sex ==’m’?0:1); } void Register(char *Name, int Age, char Sex); void GetName(char *Name); int GetAge(); char GetSex(); }; 在创建一个对象时,系统自动调用对象所属类的构造函数。如果定义了带有参数的构造 函数,就可以在声明对象时利用实参对对象进行初始化。例如, 当遇到声明 Person personl(“Zhang3”, 19, ‘f’); 时,编译器就调用构造函数 Person :: Person(const char *, int, char); 来创建对象 person1 并用实参初始化其数据成员。注意,遇到带有关键字 extern 修饰的外部 对象声明时不调用构造函数,因为该声明属于对外部已经声明过的对象的引用性声明,这一 点和外部变量的声明相同。 对于不需通过参数初始化的成员变量,也可以使用下面的形式进行初始化: ::():(), …, () { … … } 例如 MyClass::MyClass(): m_bAlready(FALSE), m_nCount(0) { } 对象具有变量的性质,一个类的对象也可以作为另一个类的数据成员。对象成员的初始 化也可通过上述方法进行,其实质是对其构造函数的调用。 应当说明的是,如果在类中没有定义任何构造函数的话,系统会为自动地为它定义一个 形如 :: (); 的缺省构造函数。这种不带任何参数且函数体为空的构造函数就叫作缺省的构造函数。如果 在类声明中已经声明、定义了构造函数,则系统不再提供缺省的构造函数。 与构造函数相对应,析构函数(Destructor)用于撤消一个对象。析构函数的声明格式 为:
第7单元类和对象(I) 139 ~0 当一个对象的生存期结束时,系统将自动地调用析构函数来撤消该对象,返还它所占据 的内存空间 定义析构函数时应注意 1.析构函数名与类名相同,只是它的前边须冠以波浪号“~”以与构造函数区别开来 2.析构函数不得带有任何参数,即其参数表必须为空,即使关键字vod也不允许有。 因此,析构函数不能重载 3.析构函数不得返回任何值,即使关键字vod也不允许有 2.在析构函数中不得调用C++的库函数exit(),这是因为该函数会做一些清理工作, 包括撤消对象,困此,在析构函数中调用该函数时,该函数为撤消对象又会调用析构函数, 从而形成无限递归。如果必须在析构函数中终止整个程序的运行,则应调用库函数 abort () 析构函数一般用来作一些销毁对象前的扫尾工作,如用 delete运算符释放动态分配的存 储等。 与构造函数一样,如果在类的声明中没有显式地定义析构函数,则系统将自动产生一个 形如 ~0 的缺省析构函数,其函数体为空 74对象与指针 可以声明指向对象的指针,方法与声明指向变量的指针相同。例如 Person personl(“ Zhang3”,19,f) 通过指针访问对象的成员要用运算符“->”,例如 ptr->GetAge o 在第6单元中介绍的new运算符可以用来动态建立一个对象,如 Person *pErson new Person 当然,用new建立的对象要用 delete释放: delete pErson 用new建立一个对象时,可调用类的构造函数。例如 Person* pErson= new Person(“ Zhang3”,19,“f”)
第 7 单元 类和对象(I) - 139 - ::~(); 当一个对象的生存期结束时,系统将自动地调用析构函数来撤消该对象,返还它所占据 的内存空间。 定义析构函数时应注意: 1.析构函数名与类名相同,只是它的前边须冠以波浪号“~”以与构造函数区别开来。 2.析构函数不得带有任何参数,即其参数表必须为空,即使关键字 void 也不允许有。 因此,析构函数不能重载。 3.析构函数不得返回任何值,即使关键字 void 也不允许有。 2. 在析构函数中不得调用 C++的库函数 exit(),这是因为该函数会做一些清理工作, 包括撤消对象,困此,在析构函数中调用该函数时,该函数为撤消对象又会调用析构函数, 从而形成无限递归。如果必须在析构函数中终止整个程序的运行,则应调用库函数 abort ()。 析构函数一般用来作一些销毁对象前的扫尾工作,如用 delete 运算符释放动态分配的存 储等。 与构造函数一样,如果在类的声明中没有显式地定义析构函数,则系统将自动产生一个 形如: ::~() { } 的缺省析构函数,其函数体为空。 7.4 对象与指针 可以声明指向对象的指针,方法与声明指向变量的指针相同。例如 Person personl(“Zhang3”, 19, ‘f’); Person *ptr = &person1; 通过指针访问对象的成员要用运算符“->”,例如: ptr->GetAge(); 在第 6 单元中介绍的 new 运算符可以用来动态建立一个对象,如 Person *pPerson = new Person; 当然,用 new 建立的对象要用 delete 释放: delete pPerson; 用 new 建立一个对象时,可调用类的构造函数。例如 Person *pPerson = new Person(“Zhang3”, 19, ‘f’);
第7单元类和对象(I) 当对象接收到一个消息时,便会调用相应的成员函数。这时,系统会向该成员函数传递 一个隐含的参数,即指向该对象自身的指针,即this指针。一般来说,this指针用途不大 因为在成员函数中可以直接使用本对象内部的所有成员变量和成员函数。但在某些场合中调 用其他类的成员函数时,可能需要传送本对象的地址。在 Windows程序设计中这种情况很多 如第8单元中,说明设备环境变量时就需要用到窗口对象的this指针。 除了指向对象的指针外,也可以说明对对象的引用。使用对象指针和对象引用的主要目 的是为了在函数间传递对象时提高程序的运行效率。 自学内容 75 const对象与 const成员函数 与普通变量一样,可使用关键字 const修饰对象。C++规定,对于cons对象,只能访问 其中也用 const修饰的成员函数,即 const成员函数。C++规定,在 const成员函数中不得 修改类中的任何数据成员的值。例如 class MyClass int x public MyClass (int a =0): x(a) int Normal O return +tx int Const Funco const return 其中成员函数 Const func()就是 const成员函数。请注意修饰符 const的位置。在其他地方 (如主函数中)声明了一个 MyClass类的 const对象: const MyClass ConstOb j(3) 则调用 int i ConstOb j. ConstFuncO
第 7 单元 类和对象(I) - 140 - 当对象接收到一个消息时,便会调用相应的成员函数。这时,系统会向该成员函数传递 一个隐含的参数,即指向该对象自身的指针,即 this 指针。一般来说,this 指针用途不大, 因为在成员函数中可以直接使用本对象内部的所有成员变量和成员函数。但在某些场合中调 用其他类的成员函数时,可能需要传送本对象的地址。在 Windows 程序设计中这种情况很多, 如第 8 单元中,说明设备环境变量时就需要用到窗口对象的 this 指针。 除了指向对象的指针外,也可以说明对对象的引用。使用对象指针和对象引用的主要目 的是为了在函数间传递对象时提高程序的运行效率。 自学内容 7.5 const 对象与 const 成员函数 与普通变量一样,可使用关键字 const 修饰对象。C++规定,对于 const 对象,只能访问 其中也用 const 修饰的成员函数,即 const 成员函数。 C++规定,在 const 成员函数中不得 修改类中的任何数据成员的值。例如 class MyClass { int x; public: MyClass(int a = 0): x(a) { } int NormalFunc() { return ++x; } int ConstFunc() const { return x+1; } }; 其中成员函数 ConstFunc()就是 const 成员函数。请注意修饰符 const 的位置。在其他地方 (如主函数中)声明了一个 MyClass 类的 const 对象: const MyClass ConstObj(3); 则调用 int i = ConstObj.ConstFunc();