1、,C+程序设计(第2版),第4章 类与对象,面向对象程序设计概述,面向对象的程序设计方法,简称为OOP,其基本要求是将描述某一类事物的数据与处理这些数据的函数都封装成一个整体,从而达到数据隐藏(保护)的目的。应用程序只有通过这一整体中的函数才能修改这一整体中的数据,同样,数据结构的变化,仅影响封装在一起的函数。这种将数据与处理这些数据的函数封装成一个整体,就构成一个类。类的封装性是指:使类中的数据在类的外部是不可见的,外部只能通过公共接口(类中的函数)与类中数据发生联系。类的派生性是指:一个类可以派生出它的子类,子类可以从它的父类中部分或全部地继承各种函数和数据,并增加新的函数和数据。,对象是
2、类的一个实例,类是用户定义的一种类型,具有为类类型的变量称为对象,也称为实例。对象是动态产生和动态消亡的。一个对象占用计算机内存中的一个独立区域,用于存放对象的数据和函数,这个区域相对独立,只有局部于对象中的函数才可以访问存放在该对象中的数据。,类是对某一事物的抽象描述,它既可包含描述事物的数据,又可包含处理这些数据的函数。在C+中,我们可以通过类的定义,把描述矩形的结构和处理这个矩形的函数组合到一个完整的类定义中: class CRectangle int left;int top;int right;int bottom;void DrawRectangle(void); ;left, t
3、op,right,bottom称为成员变量,而函数称为类的成员函数。由此可见,类中不仅包含了描述构造类型的成员变量,而且可以包含用于处理这些成员变量的操作。,4.4.1 类的定义,C+中完整的类定义形式如下:class 类名 private:私有类成员;public:公有类成员;protected:保护类成员; ;,private: 限定为类的私有成员,只允许该类中的成员函数存取私有的成员数据,对于私有的成员函数,只能被该类中的成员函数调用;,public: 限定为类的公有成员,它不仅允许该类中的成员函数调用公有成员数据,而且允许该类之外的函数也调用公有成员数据,公有成员函数不仅能被该类的成员
4、函数调用,而且能被其他函数调用。,protected: 限定为类的保护成员,允许该类及其派生类的成员函数调用成员数据或成员函数。但其他函数不能存取该类的保护成员数据,也不能调用该类的保护成员函数。,例4.1 定义描述一个人的类。 class person private:char name12;int age;char sex4;public:void RegisterPerson(char *, int, char *);void GetName(char *);int GetAge(void);void GetSex(char *); ;,在类person中,把成员数据name、age、s
5、ex定义为私有成员; 四个函数定义为公有成员: RegisterPerson( ):用来登录一个人的姓名、性别和年龄; GetName( ):用来获取一个人的姓名; GetAge( )和GetSex( )获取人的年龄和性别。,在类体定义中,当省略关键字private时,系统默认为所定义的成员数据为私有成员,所以类person还可以有另外的等同写法。,在以上的类定义中,person类中的成员函数仅给出了函数原型,并没有给出成员函数的定义,在使用这些成员函数前,必须先给出函数的定义。在C+中定义成员函数的一般格式为: 函数类型 类名:函数名(参数表) 其中,运算符“:”称为作用域分辨符,它指出“函
6、数名”是属于“类名”的成员函数。,void Person:RegisterPerson(char *newname, int newage, char *newsex) strcpy(name, newname);age=newage;strcpy(sex,newsex); Void Person:GetName(char *newname) strcpy(name, newname); Int Person:GetAge(void) return(age); Void Person:GetSex(char *newsex) strcpy(sex, newsex); ,例4.2 定义类pers
7、on的四个成员函数,类具有封装性,并且类只是定义了一种结构-模板,所以类中的任何成员数据均不能使用关键字extern、auto、register限定其存储类型。 成员函数可以直接使用类中的任一成员,包括数据成员和函数成员。 在定义类时,只是定义了一种导出的数据类型,并不为类分配存储空间,所以在定义类中的数据成员时,不能对其初始化。如:class testint tx=5, y=6; extern float x; (4) 在定义一个类时,对其成员指定访问权限的原则是:若定义的成员限于该类的成员函数使用时,应指定其为私有成员;若允许类外使用该成员时,应将其访问权限定义为公有的。,在定义一个类时,
8、须注意以下几点:,在定义一个类时,其成员函数的函数体定义也可以在定义类的类体中直接定义,即在类中直接定义成员函数称为内联函数。,定义内联成员函数的另一种方法是,在类体中只是给出成员函数的原型说明,在类体外定义成员函数时,与定义一般的内联函数一样,在成员函数的定义前面加上关键定inline。,内联(inline)成员函数,内联函数与一般函数的区别是,编译器在生成目标代码时,在调用这个函数的位置装上这个函数的实际代码,而不是处理成一个函数调用。因此可以加快程序的运行速度,缺点是占用内存空间。所以在C+中如果定义的函数是一个小函数,在整个代码调用它地方又相对少,就应该用inline定义为内联函数。,
9、class person private:char Name12;int Age;char Sex4;public:void RegisterPerson(char *name, int age, char *sex) strcpy(Name, name);Age=age;strcpy(Sex, sex);void GetName(char *);int GetAge(void);void GetSex(char *); ; inline void Person:GetName(char *name) strcpy(name, Name); .,例4.3 在类体中直接定义成员函数的函数体,存储
10、类型 类名 对象列表; 如:person P1, P2; 说明了两个对象P1和P2,它们均是类person的对象。在程序执行时,通过为对象分配存储空间来创建对象,在创建对象时,被用作样板。参见下表:,4.1.3 对象的创建与使用,对象是类的一个实例,对象必须先定义然后才能使用。说明对象的一般格式为:,表4-1 类person的两个实例P1和P2,为了减少成员函数所占用的空间,在建立对象时,只为对象分配用于保存成员数据的内存空间,而成员函数的代码为该类的每一个对象所共享。类中定义的成员函数的代码被放在计算机内存的一个公用区中,并供该类的所有对象共享。但逻辑上,我们仍将每一个对象理解为由独立的成员
11、数据和各自的成员函数代码组成。,有两种方法可存储对象。,图4.1 各对象完全独立地安排内存的方案,数据区,对象1,数据区,对象,数据区,对象,图4.2 各对象的代码区共用的方案,公共代码区,图4.1对应的是在类说明中定义函数,而图4.2对应的是在类说明外部定义函数 。如果在类外定义函数并使用inline,则系统也会采用内联扩展方式实现,每个对象都有该函数的一份独立的代码。,方法1: class A public:int r, t; ; A x, y;,方法2: class Bint I, j; public:void Setdata(int a, int b) I=a; j=b;void pr
12、int(void)coutItjn; b1, b2;,方法3: class public:int c, d; t1, t2=100, 200,类对象的初始化仅限于对类的公有成员数据,如方法3。当类中的成员数据被指定为私有或保护类型时,定义对象时不允许对它的成员数据进行初始化操作,如: B b3=500, 600; 是非法的。实际上私有成员只能由类的成员函数对其数据进行操作,而初始化显然是一个外部过程,所以这样的操作是非法的。另外,类对象中成员数据的初始化操作一般是由构造函数或拷贝初始化构造函数来实现。,在定义了类的对象以后,可以使用成员运算符“.” 来访问类的成员。但对于公有成员、私有成员及保
13、护成员,在使用上存在差异。,#include #include Class CRect private:int left,top; /矩形的左上角坐标public:int right,bottom; /矩形的右下角坐标void setcoord(int,int,int,int); /设置坐标值的函数void getcoord(int *L, int *R, int *T, int *B) /取坐标值的函数 *L=left; *R=right;*T=top; *B=bottom;void Print(void) cout“area=“fabs(right-left)*fabs(bottom-to
14、p)n; ;,例:描述一个矩形对象,设置矩形的坐标,并输出其相应的坐标值。,void CRect:setcoord(int L, int R, int T, int B) left=L; right=R;top=T; bottom=B; void main(void) CRect r, rr;int a, b, c, d;r.setcoord(100,300,50,200);cout“right=”r.rightn;cout“bottom=”r.bottomn;r.getcoord( ,结果为: right=300; bottom=200; left=100; top=50; area=300
15、00; area=30000;,几点说明: 在类的外部,只能访问类的公有成员,而不能访问类的私有成员或保护成员。 例如:a=r.left; c=r.top; 都是不允许的,因为成员left和top都是私有成员。若要访问类对象的私有成员,只能通过对象的公有成员函数来获取。如例中的r.getcoord( 将对象r的所有成员依次赋给对象rr的成员,这种赋值与对象的成员访问权限无关。 3. 对象用作函数的参数时,属于赋值调用;函数也可以返回一个对象。,在类的作用域中说明的标识符(类的数据成员)仅在该类的类作用域内有效,即这种标识符的可见性在类的作用域之内。class Dpublic:int I, l;
16、void Print(void) cout“I=“In; ; I = 35; /错:公有成员数据 I 在class D的内部,外部不可见,只有通过类的对象才可引用。 改为:class D a;couta.Iendl;,类的作用域,3. 在函数(块)之外定义的类,其类名的作用域为文件作用域;在函数体内定义的类,其类名的作用域为块作用域。,2. 类的成员函数,不论是内联还是非内联的,其函数名的作用域都是属于类的作用域范围。因此不能直接通过函数名来调用成员函数。例如:Print( ); /错:,4. 类的说明分为引用说明和定义性说明。在说明一个类时,没有给出类体而仅给出类名时,属于类的引用性说明。引用性说明类不能用来建立对象,只能用来说明函数的形参、指针和引用。例如: Class CC;CC c1; /错:类CC只是引用性说明,不能用来建立对象。 Class XCC c3, c4; /错:原因同上。CC *pc; /正确:说明一个指针。 ;,