420 likes | 536 Views
面向对象的程序设计. 第 2 章 类和对象. 现实生活中: 对象是由一组属性和一组行为构成的。 按钮对象: 按钮的内容、大小,按钮的字体、图案等等 针对按钮的各种操作,创建、单击、双击、拖动等. 班级对象: 班级的静态特征,所属的系和专业、班级的人数,所在的教室等。这种静态特征称为属性; 班级的动态特征,如学习、开会、体育比赛等,这种动态特征称为行为。. 任何一个对象都应当具有这两个要素,一是属性 (attribute) ;二是行为 (behavior) ,即能根据外界给的信息进行相应的操作。 对象是由一组属性和一组行为构成的。.
E N D
面向对象的程序设计 第2章 类和对象
现实生活中:对象是由一组属性和一组行为构成的。现实生活中:对象是由一组属性和一组行为构成的。 按钮对象: 按钮的内容、大小,按钮的字体、图案等等 针对按钮的各种操作,创建、单击、双击、拖动等 班级对象: 班级的静态特征,所属的系和专业、班级的人数,所在的教室等。这种静态特征称为属性; 班级的动态特征,如学习、开会、体育比赛等,这种动态特征称为行为。
任何一个对象都应当具有这两个要素,一是属性(attribute);二是行为(behavior),即能根据外界给的信息进行相应的操作。对象是由一组属性和一组行为构成的。 面向对象的程序设计采用了以上人们所熟悉的这种思路。使用面向对象的程序设计方法设计一个复杂的软件系统时,首要的问题是确定该系统是由哪些对象组成的,并且设计这些对象。在C++中,每个对象都是由数据和函数(即操作代码)这两部分组成的。
我们可以对一个对象进行封装处理,把它的一部分属性和功能对外界屏蔽,也就是说从外界是看不到的、甚至是不可知的。 使用对象的人完全可以不必知道对象内部的具体细节,只需了解其外部功能即可自如地操作对象。 把对象的内部实现和外部行为分隔开来。
传统的面向过程程序设计是围绕功能进行的,用一个函数实现一个功能。所有的数据都是公用的,一个函数可以使用任何一组数据,而一组数据又能被多个函数所使用。程序设计者必须考虑每一个细节,什么时候对什么数据进行操作 。 面向对象程序设计采取的是另外一种思路。它面对的是一个个对象。实际上,每一组数据都是有特定的用途的,是某种操作的对象。也就是说,一组操作调用一组数据。
程序设计者的任务包括两个方面:一是设计所需的各种类和对象,即决定把哪些数据和操作封装在一起;二是考虑怎样向有关对象发送消息,以完成所需的任务。各个对象的操作完成了,整体任务也就完成了。程序设计者的任务包括两个方面:一是设计所需的各种类和对象,即决定把哪些数据和操作封装在一起;二是考虑怎样向有关对象发送消息,以完成所需的任务。各个对象的操作完成了,整体任务也就完成了。 因此人们设想把相关的数据和操作放在一起,形成一个整体,与外界相对分隔。这就是面向对象的程序设计中的对象。
在面向过程的结构化程序设计中,人们常使用这样的公式来表述程序∶在面向过程的结构化程序设计中,人们常使用这样的公式来表述程序∶ 程序=算法+数据结构
面向对象的程序组成: 对象 = 算法 + 数据结构 程序=(对象+对象+对象+……)+ 消息 消息的作用就是对对象的控制。 程序设计的关键是设计好每一个对象以及确定向这些对象发出的命令,使各对象完成相应的操作。
每一个实体都是对象。有一些对象是具有相同的结构和特性的。每一个实体都是对象。有一些对象是具有相同的结构和特性的。 每个对象都属于一个特定的类型。 在C++中对象的类型称为类(class)。类代表了某一批对象的共性和特征。类是对象的抽象,而对象是类的具体实例(instance)。
类的定义 类是一种复杂的数据类型,它是将不同类型的数据和与这些数据相关的运算封装在一起的 集合体。 类将一些数据及与数据相关的函数封装在一起,使类中的数据得到很好的“保护”。在大型程序中不会被随意修改。
class Student { private : char Name[20]; float Math; float Chiese; public : float average; void SetName(char *name); void SetMath(float math); void SetChinese(float ch); float GetAverage(void); }; 类的定义格式: class 类名 { private : 成员数据; 成员函数; public : 成员数据; 成员函数; protected: 成员数据; 成员函数; }; 类名 关键字 私有 公有 保护 分号不能少
用关键字priviate限定的成员称为私有成员,对私有成员限定在该类的内部使用,即只允许该类中的成员函数使用私有的成员数据,对于私有的成员函数,只能被该类内的成员函数调用;类就相当于私有成员的作用域。
用关键字public限定的成员称为公有成员,公有成员的数据或函数不受类的限制,可以在类内或类外自由使用;对类而言是透明的。用关键字public限定的成员称为公有成员,公有成员的数据或函数不受类的限制,可以在类内或类外自由使用;对类而言是透明的。
而用关键字protected所限定的成员称为保护成员,只允许在类内及该类的派生类中使用保护的数据或函数。即保护成员的作用域是该类及该类的派生类。而用关键字protected所限定的成员称为保护成员,只允许在类内及该类的派生类中使用保护的数据或函数。即保护成员的作用域是该类及该类的派生类。
每一个限制词(private等)在类体中可使用多次。一旦使用了限制词,该限制词一直有效,直到下一个限制词开始为止。每一个限制词(private等)在类体中可使用多次。一旦使用了限制词,该限制词一直有效,直到下一个限制词开始为止。 如果未加说明,类中成员默认的访问权限是private,即私有的。
class Student { char Name[20]; float Math; float Chiese; public : float average; void SetName(char *name); void SetMath(float math); void SetChinese(float ch); float GetAverage(void); }; 均为私有权限 均为公有权限
class A { float x, y; public: void Setxy(float a,float b) { x=a; y=b; } void Print(void) { cout<<x<<‘\t’<<y<<endl; } }; A 私有数据 公有函数 在类外不能直接使用 x 或 y ,必须通过Setxy()给 x 或 y 赋值,通过Print()输出 x 或 y 。
在C++语言中,结构体类型只是类的一个特例。结构体类型与类的唯一的区别在于:在类中,其成员的缺省的存取权限是私有的;而在结构体类型中,其成员的缺省的存取权限是公有的。
对象的定义方法同结构体定义变量的方法一样,也分三种,当类中有数据成员的访问权限为私有时,不允许对对象进行初始化。对象的定义方法同结构体定义变量的方法一样,也分三种,当类中有数据成员的访问权限为私有时,不允许对对象进行初始化。 class A { float x,y; public: void Setxy( float a, float b ){ x=a; y=b; } void Print(void) { cout<<x<<‘\t’<<y<<endl; } } a1,a2; 定义全局对象 void main(void) { A a3,a4; } 定义局部对象
对象的使用 一个对象的成员就是该对象的类所定义的成员,有成员数据和成员函数,引用时同结构体变量类似,用“.”运算符。
a1 x y m n Setxy() Print() a2 x y m n Setxy() Print() class A { float x,y; public: float m,n; void Setxy( float a, float b ){ x=a; y=b; } void Print(void) { cout<<x<<‘\t’<<y<<endl; } }; 2.0 5.0 10 20 void main(void) { A a1,a2; //定义对象 a1.m=10; a1.n=20; //为公有成员数据赋值 a1.Setxy(2.0 , 5.0); //为私有成员数据赋值 a1.Print(); } 输出: 2 5
成员函数与成员数据的定义不分先后,可以先说明函数原型,再在类体外定义函数体。成员函数与成员数据的定义不分先后,可以先说明函数原型,再在类体外定义函数体。 class A { float x, y; public: void Setxy(float a,float b) { x=a; y=b; } void Print(void) { cout<<x<<‘\t’<<y<<endl; } }; 在类体内定义成员函数
class A { float x, y; public: void Setxy(float a,float b); void Print(void); }; 在类体内说明成员函数原型 void A::Setxy(float a,float b) { x=a; y=b; } void A::Print(void) { cout<<x<<‘\t’<<y<<endl; } 在类体外定义成员函数
在类体外定义成员函数的格式: <type> < class_name > :: < func_name > (<参数表>) { ...... //函数体 } 类名 函数类型 函数名 形参列表 void A::Setxy(float a,float b) { x=a; y=b; } 函数体
在定义一个类时,要注意如下几点: 1、类具有封装性,并且类只是定义了一种结构(样板),所以类中的任何成员数据均不能使用关键字extern,auto或register限定其存储类型。 2、在定义类时,只是定义了一种导出的数据类型,并不为类分配存储空间,所以,在定义类中的数据成员时,不能对其初始化。如: class Test { int x=5,y=6; //是不允许的 extern float x; //是不允许的 }
内联成员函数 当我们定义一个类时,可以在类中直接定义函数体。这时成员函数在编译时是作为内联函数来实现的。 同时,我们也可以在类体外定义类的内联成员函数,在类体内说明函数,在类体外定义时,在成员函数的定义前面加上关键字inline。 class A { float x, y; public: void Setxy(float a,float b); void Print(void); }; 说明该成员函数为内联 inline void A::Setxy(float a,float b) { x=a; y=b; } inline void A::Print(void) { cout<<x<<‘\t’<<y<<endl; }
2.3.4 成员函数的存储方式 在定义类时,只是定义了一种数据类型,即说明程序中可能会出现该类型的数据,并不为类分配存储空间。 只有在定义了属于类的变量后,系统才会为类的变量分配空间。 类的变量我们称之为对象。 对象是类的实例,定义对象之前,一定要先说明该对象的类。
Student st1,st2; 类名 对象名 Q:若同一类的10个对象,为所有的数据和函数代码分配空间么? A: 不同对象占据内存中的不同区域,它们所保存的数据各不相同,但对成员数据进行操作的成员函数的程序代码均是一样的。 在建立对象时,只为对象分配用于保存数据成员的内存空间,而成员函数的代码为该类的每一个对象所共享。 P53例子 Q: 调用不同对象的成员函数都是执行同一段函数代码,执行结果为什么不同? A: this 指针,可以指向不同的对象,不会混淆。详见第3章。
用成员选择运算符“.”只能访问对象的公有成员,而不能访问对象的私有成员或保护成员。若要访问对象的私有的数据成员,只能通过对象的公有成员函数来获取。
2.4 对象成员的引用 • 访问对象的成员可以有3种方法 • 对象名.成员名 • 通过指向对象的指针访问对象中的成员 • 通过对象的引用访问对象中的成员 • 见教材P55的例子
class A { float x,y; public: float m,n; void Setxy( float a, float b ){ x=a; y=b; } void Print(void) { cout<<x<<‘\t’<<y<<endl; } }; 必须通过类内公有函数访问私有数据成员 void main(void) { A a1,a2; a1.m=10; a1.n=20; //为公有成员数据赋值 a1.x=2; a1.y=5; a1.Setxy(2.0,5.0); a1.Print(); } 非法,私有成员不能在类外访问
同类型的对象之间可以整体赋值,这种赋值与对象的成员的访问权限无关。同类型的对象之间可以整体赋值,这种赋值与对象的成员的访问权限无关。 class A { float x,y; public: float m,n; void Setxy( float a, float b ){ x=a; y=b; } void Print(void) { cout<<x<<‘\t’<<y<<endl; } }; void main(void) { A a1,a2; a1.m=10; a1.n=20; //为公有成员数据赋值 a1.Setxy(2.0,5.0); a2=a1; a1.Print(); a2.Print(); } 同类型的对象之间可以整体赋值 相当于成员数据间相互赋值
对象可以作函数的入口参数(实参、形参),也可以作函数的出口参数。这与一般变量作为函数的参数是完全相同的。 可以定义类类型的指针,类类型的引用,对象数组,指向类类型的指针数组和指向一维或多维数组的指针变量 一个类的对象,可作为另一个类的成员
类作用域、类类型的作用域和对象的作用域 类体的区域称为类作用域。类的成员函数与成员数据,其作用域都是属于类的作用域,仅在该类的范围内有效,故不能在主函数中直接通过函数名和成员名来调用函数。
class A { float x,y; public: float m,n; void Setxy( float a, float b ){ x=a; y=b; } void Print(void) { cout<<x<<‘\t’<<y<<endl; } }; void main(void) { A a1,a2; m=20; n=10; Setxy(2.0, 5.0); Print(); } void main(void) { A a1,a2; a1.m=20; a1.n=10; a1.Setxy(2.0, 5.0); a1.Print(); } 不能直接调用 用对象名调用
类类型的作用域:在函数定义之外定义的类,其类名的作用域为文件作用域;而在函数体内定义的类,其类名的作用域为块作用域 。 对象的作用域与前面介绍的变量作用域完全相同 , 全局对象、局部对象、局部静态对象等。
class A { float x,y; public: float m,n; void Setxy( float a, float b ){ x=a; y=b; } void Print(void) { cout<<x<<‘\t’<<y<<endl; } }a3,a4; 类A:文件作用域,在整个文件中有效 全局对象 void main(void) { A a1,a2; class B{ int i,j; public : void Setij(int m, int n){ i=m; j=n; } }; B b1,b2; a1.Setxy(2.0, 5.0); b1.Setij(1,2); } 局部对象 类B:块作用域,在函数内有效。
定义类的指针及如何用指针来引用对象 a1 2.0 class A{ float x,y; public: float Sum(void) { return x+y; } void Set(float a,float b) { x=a; y=b;} void Print(void) { cout<<"x="<<x<<'\t'<<"y="<<y<<endl; } }; void main(void) { A a1,a2; A *p; //定义类的指针 p=&a1; //给指针赋值 p->Set(2.0, 3.0); //通过指针引用对象的成员函数 p->Print(); cout<<p->Sum()<<endl; a2.Set(10.0, 20.0); a2.Print(); } p 3.0
定义类的数组及数组中元素的引用 pstu stu void main(void) { Stu stu[3]; //定义类的数组 Stu *pstu; //定义类的指针 pstu=stu; //为指针赋值 int i; stu[0].SetStudent (“A”,90,90);//通过数组元素的引用赋值 stu[1].SetStudent ("B",80,80); stu[2].SetStudent ("C",70,70); for(i=0;i<3;i++) { pstu->Show ();//指针变量指向数组元素 pstu++; //指针变量加一,指向下一元素 } } pstu
2.5 类和对象的简单应用举例 • 教材P59末结论 • 例2.3 • 例2.4
2.6 类的封装性和信息隐蔽 • 2.6.1 公有接口与私有实现的分离 • 2.6.2 类声明和成员函数定义的分离 • 将类的声明(包括成员函数的声明)放在指定的头文件中,用户使用该类,只要把相关的头文件包含进来。 • 类的声明(头文件),类的成员函数实现文件(.cpp),主函数文件