2009-7-28 1
第 9章类和对象
2009-7-28 2
为什么定义类? 把现实世界的事物归纳成类,以贴近自然的方式描述现实系统。 类是结构的扩展,
可用来对外部函数访问作限制。
概述局部变量隐藏在函数体内;通过函数的参数传递共享变量。
使用静态全局变量,局限于文件。
数据量大时,效率低使用全局变量,程序中每个函数都可以访问。 查错困难
2009-7-28 3
C++引入类解决上述问题(限制全局变量使用和构成新类型)
外部函数可直接访问结构的成员。
破坏了数据的封装特性
2009-7-28 4
类的组成?类名,数据成员 (属性,data
member),成员函数 (服务,member function)。
什么是类?将数据与处理这些数据的函数封装成一个整体,即构成了 类 。 封装可以解决数据与函数代码之间的相容性和数据的保护问题。
类与结构不同之处是成员访问说明符。
除该类的成员函数外,
谁都不得访问程序中所有函数都可访问
p u b lic
p r o te c te d
p r iv a te
访问说明符
2009-7-28 5
注,1.三个关键字的顺序无关紧要,且可多次使用。
2.一个关键字的访问权限自紧跟关键字后的第一个成员起,至出现另一个限定访问权限的关键字止。
3.类体中成员的顺序不限,建议成员数据集中在类体的前面定义,成员函数集中在类体的后面定义。
proceted所限定的成员为保护成员,它允许该类成员及该类的派生类成员函数存取保护成员数据或调用保护成员函数。
类的实例称为 对象 。
2009-7-28 6
类的定义?
class className //以关键字 class 开头
{
public,//类的公有成员
data members; //公有数据成员
member functions; //公有成员函数
protected,//类的保护成员
data members; //保护数据成员
member functions; //保护成员函数
private,//类的私有成员
data members; //私有数据成员
member functions; //私有成员函数
}; //以括号及分号结束,体现封装缺省时为私有成员 (private)
{}中为 类体类中定义的数据和函数称为类的成员
2009-7-28 7
例 1,定义描述一个人的类。
分析,描述一个人的特征用姓名、性别、年龄来表示。用函数 RegisterPerson( )登录一个人的姓名、性别和年龄;用函数 getName( )来获取一个人的姓名;
用另外两个函数 GetAge( )和 GetSex( )来获取一个人的年龄和性别。 91
也可省略为如下定义:
911
912
如下定义时不可省略:
2009-7-28 8
类的成员函数定义一般格式:
数据类型 类名,:成员函数名 <参数表 > {
……
}
例 2,定义 Person的四个成员函数。
92
2009-7-28 9
注,1.类中任何成员数据不能使用关键字 extern、
auto或 register限定其存储类型。
2.成员函数可直接使用类中的任一成员。
3.在定义类中的数据成员时,不能对其初始化。如:
class Test{
int x=5,y=6; //错误
extern float x; //错误

}
4.若定义的成员限于该类的成员函数使用时,
应指定为私有成员,若允许类外使用时,应定义为公有的。
2009-7-28 10
例 3,定义并实现类 student。
#include<iostream.h>
class student{ //定义类
private,//类私有成员
float score;
public,//类的公有成员
void input(float s) {score=s;}
void modify(float s) {score=s;}
void display( ) {
cout << "\n score,"<< score; }
};
void main( ) { //主函数
student ss; //声明对象
ss.input(88.5); //调用成员函数
ss.display( ); }
2009-7-28 11
问题? 类的定义和实现放在一起好不好?
结论? 不好!
方法?类的定义放在 student.h
类的实现放在 student.cpp
优点?1.把类的定义与实现分离开来,便于文档管理、维护。
2.可将类的实现隐蔽起来,使软件开发商能独立开发软件。
3.便于团体式的大型软件开发。
2009-7-28 12
例 4,文件 student.h:
#ifndef STUDENT_H //条件编译
#define STUDENT_H
class student //定义类
{private,//类私有成员
int number; //学号
char *name; //姓名
float score; //成绩
public,//类的公有成员
void input(int n,char *na,float s);
void modify(float s);
void display( );
};
#endif
2009-7-28 13
文件 student.cpp:
#include<iostream.h>
#include <string.h>
#include "student.h" //必须包含类的定义
void student::input(int n,char *na,float s)
{ number=n;
score=s;
name=new char[strlen(na) + 1];
strcpy(name,na); }
void student::modify(float s) {score=s;}
void student::display( )
{ cout << "\n number,"<< number;
cout << "\n name:"<< name;
cout <<,\n score:,<< score; }(续下 )
2009-7-28 14
(续上 )
void main( ) //主函数
{
student ss; //声明对象
char na[]= " WANG QIAN ";
ss.input(9901,na,88.5); //调用成员函数
ss.display( );
}
结果,number,9901
name,WANG QIAN
score,88.5
注意,函数 input的作用为初始化。
2009-7-28 15
例 4 总 结
1.有两个文件:
student.h (类的头文件)
student.cpp (类的实现文件)
2.类的成员函数实现方法是用,,引导。
如:
void student::display( ) {……}
3.在头文件中应加入 条件编译,以免多次包含,
如,#ifndef STUDENT_H …...
2009-7-28 16
类和结构体类型类和结构体类型的相同点:
1.成员均可是数据成员或函数成员;
2.均可使用关键字 private,public和 protected。
类和结构类型的不同点:
类中成员的缺省权限是私有的,而结构体类型中成员缺省权限是公有的。
结构体类型是类的一个特例。
一般只需描述结构时用结构体,既要描述数据又要描述对数据进行处理的方法时使用类。
2009-7-28 17
例 5,定义一个三角形的结构体,结构体中包含成员函数。
9
3执行后输出:
三角形的三条边长分别是:
3 4 5
三角形的面积为,6
三角形的三条边长分别是:
7 5 5
三角形的面积为,12.4975
2009-7-28 18
内联成员函数可在类中直接定义成员函数。
例 6,在类体中直接定义成员函数的函数体。 94
两种定义方法的区别,在类体中直接定义成员函数时,这种成员在编译时是作为内联函数来实现的,
称这种成员函数为 内联成员函数 ;而前面定义的不作为内联函数实现。
2009-7-28 19
一般函数功能较简单时用直接定义成员函数的方法,否则分开定义。类外定义成员函数时,在成员函数的定义前加关键字 inline来定义内联成员函数。
例 7,将类 Person中的成员函数定义为内联成员函数。
95
2009-7-28 20
对象具有类类型的变量称为 对象,或称 类的实例 为对象。
对象的说明对象必须先定义后使用,说明对象的一般格式:
存储类型 类名 对象名 1《,对象名 2…,;
如,Person p1,p2;
//定义了两个类 Person的对象 p1和 p2
2009-7-28 21
例 7,对象的声明及成员函数调用。
#include "student.h "
void main( )
{ student s2; //声明
s2.input(9902,"Li ",90);
s2.display( ); //公用成员函数
s2.modify(95); //公用成员函数
s2.display( ); }
结果,number,9902
name,Li
score,90
number,9902
name,Li
score,95
2009-7-28 22
三种定义对象的方法:
1.先定义类的类型,再说明对象;
2.在定义类的同时说明对象;
3,直接说明对象,而不定义类的类名。
例 8,用三种方法定义对象。
96
类中成员数据的访问权限为公有时,定义对象时允许对它的成员数据进行初始化,否则不允许。
类中成员数据的初始化通常采用定义构造函数或采用拷贝初始函数的方法来实现。后面介绍。
2009-7-28 23
对象的使用对象的作用与结构体变量的使用方法类同。但三种不同的成员使用上有差异。
例 9,描述一个矩形对象,设置矩形的坐标,并输出其相应的坐标值。
分析,一个矩形可用左上角和右下角的两个坐标点来描述,左上角坐标用( left,top)来表示,右下角的坐标用( right,bottom)来描述。假定这四个数的取值均是大于 0的正整数。
97
2009-7-28 24
执行后输出:
right=300
bottom=200
left=100
top=50
面积 =30000
面积 =30000
注,1.用成员选择符,.”来访问对象的成员。当访问一个成员函数时,也称为 向对象发送一个消息 。运算符,.”只能访问对象的公有成员,对私有成员须通过对象的公有成员函数来获取。
2.同类型的对象之间可以整体赋值。如:
rr=r;
赋值与对象的成员的访问权限无关。
2009-7-28 25
3.对象用作函数的参数时,与结构体作函数参数相同。
4.可以定义类类型指针、类类型的引用、对象数组、
指向类类型的指针数组和指向一维或多维数组的指针变量。如:
CRect a[10],*p,b,&pb=b,*pp[5],( *pa) [4];
定义 a为对象数组,p为指向对象的指针变量,pb为对象 b的引用,pp为指向对象的指针数组,pa为指向一维数组的指针变量。
5.一个类的对象可作为另一个类的成员。如:
class A{
… // 类 A的类体定义
};
class B{
A a1,a2;

};
2009-7-28 26
类作用域、类类型的作用域和对象的作用域一般在定义类时用一对花括号将类体括起来,称为类的作用域 。
在类的作用域中说明的标识符只在类的作用域中有效。如:
class D{
public:
int I,l;
void Print(void){cout<<“I=”<<I<<?\n?; }
};
I=35; //错误 。因 I不可见,故不可对其赋值类的成员函数不可通过函数名直接调用。如:
Print( ); //错误,成员函数不可见,不可直接调用
2009-7-28 27
类的类型名的作用域同标识符的作用域。即在函数定义外定义的类,类名的作用域为 文件作用域 ;在函数体内定义的类,类名的作用域为 块作用域 。
类的说明分 引用性说明 与 定义性说明 。
引用性说明 是说明类时没有给出类体而仅给出类名。
引用性说明的类不能用来建立对象,只能说明函数的形参、指针和引用。如:
class CC;
CC C1; //错误 。类 CC是引用性说明,不对用来建立对象
class X{
CC c3,c4; //错误,因 CC是引用性说明
CC *pc; //正确,因 pc是指向类的指针

}
2009-7-28 28
类的嵌套类体中又包含类称为 类的嵌套 。如:
class Outer{
public:
class Inner{ //Inner类的作用域开始
public:
int x,y;
};
Inner c1,c2; //定义类 Inner的对象
float a,b;
}; //Inner类的作用域结束
2009-7-28 29
成员函数的重载类中的成员函数与一般函数一样,可带缺省参数,也可重载。
例 10,处理一个数组构成的线性表,动态产生线性表,并输出线性表中的数据。
98执行后输出:
线性表 list的元素个数为,0
线性表 list长度为,10
线性表 list1的元素个数为,10
线性表 list1长度为,20
0 1 2 3 4 5 6 7 8 9
现在线性表 list1中的第三个值为,100
现在线性表 list1中元素个数为,12
0 1 2 100 4 5 6 7 8 9 20 200
线性表 list1中的最后一个元素为,200
2009-7-28 30
this指针用对象的成员函数访问对象的成员时,要指明是哪个对象的成员。在成员函数体中可用关键字 this来访问。在成员函数的实现中,当访问该对象的一个成员时,均省略了 this指针。如上例中的成员函数 int GetElem(int i),
若使用 this指针,完整的程序段为:
int GetElem(int i){
if((i>=0)&&(i<this->nElem))return this->List[i];
else return 0;
}
this指针的缺省说明为:
ListClass *const this;
表示只允许成员函数体内使用该指针,但不允许改变该指针的值。
2009-7-28 31
一般情况下不必关心该指针,但特殊场合可能用到该指针的值。如若对上例增加一个拷贝线性表的成员函数 CopyList( );,其原型为:
void CopyList(ListClass);
该函数的定义为:
void ListClass::CopyList(ListClass L){
nMax=L.Max( );
nElem=L.Elem( );
if(List)delete[ ]List;
List=new int[nMax];
for(int i=0; i<nElem; i++)
List[i]=L.GetElem(i);
}
2009-7-28 32
若程序中出现:
ListClass m1;
m1.CopyList(list);
则实现了将对象 list中的线性表拷贝到对象 m1中。但若出现:
list.CopyList(list);
时,出现线性表自己拷贝到自己,此时上述程序中分配新空间以及对新空间操作就出现错误。为此将上述程序改为:
void ListClass::CopyList(ListClass L){
if(&L!=this){ //条件不成立,直接返回
nMax( );
nElem=L.Elem( );
if(List)delete[ ]List;
List=new int[nMax];
for(int i=0; i<nElem; i++)
List[i]=L.GetElem(i);
}
}
2009-7-28 33
本章小结
1 类的定义及实现,公有,保护 和 私有成员 。
2 类的 定义,实现 和 应用 可放在一个文件,
通常可分开存放。
3 类的成员被访问规则:是 OOT的基础 。
4 this 指针:指向对象自己的指针。