类和对象 潘荣江 panrj@sdu.edu.cn 山东大学计算机科学与技术学院
面向对象程序设计方法 基于过程的程序设计方法的不足
传统的面向过程程序设计是围绕功能进行的,用一个函数实现一个功能。所有的数据都是公用的,一个函数可以使用任何一组数据,而一组数据又能被多个函数所使用。程序设计者必须考虑每一个细节,什么时候对什么数据进行操作 。 面向对象程序设计采取的是另外一种思路。它面对的是一个个对象。实际上,每一组数据都有特定的用途,是某种操作的对象。也就是说,一组操作调用一组数据。
程序设计者的任务包括两个方面: 一是设计所需的各种类和对象,即决定把哪些数据和操作封装在一起; 二是考虑怎样向有关对象发送消息,以完成所需的任务。各个对象的操作完成了,整体任务也就完成了。 把相关的数据和操作放在一起,形成一个整体,与外界相对分隔。这就是面向对象的程序设计中的对象。
程序=算法+数据结构 在面向过程的结构化程序设计中,经常这样表述程序∶ 面向对象的程序组成: 对象 = 算法 + 数据结构 程序=(对象+对象+对象+……) + 消息 消息的作用就是对对象的控制。 程序设计的关键是设计好每一个对象以及确定向这些对象发出的命令,使各对象完成相应的操作。
对象 任何一个对象都应当具有两个要素,一是属性(attribute);二是行为(behavior),即能根据外界给的信息进行相应的操作。对象是由一组属性和一组行为构成的。 面向对象的程序设计采用了人们所熟悉的这种思路。使用面向对象的程序设计方法设计一个复杂的软件系统时,首要的问题是确定该系统是由哪些对象组成的,并且设计这些对象。 在C++中,每个对象都是由数据和函数(即操作代码)组成。
按钮对象: 按钮的属性:大小、字体、图案、所在位置等 按钮的操作:创建、单击、双击、拖动等 班级对象: 班级的静态特征(属性):所属的系和专业、班级的人数,所在的教室等。 班级的动态特征(行为或功能):学习、开会、体育比赛等。
抽象 每一个实体都是对象。有一些对象是具有相同的结构和特性的。每个对象都属于一个特定的类型。 在C++中对象的类型称为类(class)。类代表了某一批对象的共性和特征。类是对象的抽象,对象是类的具体实例(instance)。
封装 我们可以对一个对象进行封装处理,把它的属性和功能组合在一起,把一部分属性和功能对外界屏蔽,也就是说从外界是看不到的、甚至是不可知的。 使用对象的人完全可以不必知道对象内部的具体细节,只需了解其外部功能即可自如地操作对象。 把对象的内部实现和外部行为分隔开来。
继承与重用 如果在软件开发中已经建立了一个名为A的“类”,又想 另外建立一个名为B的“类”,而后者与前者内容基本相 同,只是在前者的基础上增加一些属性和行为,只需在 类A的基础上增加一些新内容即可。这就是面向对象程序 设计中的继承机制。利用继承可以简化程序设计。 例如,“白马”继承了“马”的基本特征,又增加了新的特征 (颜色), “马”称为父类或基类, “白马”是从“马”派生 出来的,称为子类或派生类。 C++提供了继承机制,采用继承的方法可以很方便地利用 一个已有的类建立一个新的类。这就是“软件重 用”(software reusability)的思想
多态性 如果有几个相似而不完全相同的对象,有时要求在向它 们发出同一个消息时,它们的反应各不相同,分别执行 不同的操作。这种情况就是多态现象。 例如在Windows环境下,用鼠标双击一个文件对象(这就是向对 象传送一个消息),如果对象是一个可执行文件,则会执行此程 序,如果对象是一个文本文件,则启动文本编辑器并打开该文 件。 在C++中的多态性(polymorphism): 由继承而产生的不同 类,其对象对同一消息会作出不同的响应。多态性是面 向对象程序设计的一个重要特征,能增加程序的灵活性
类也是一种数据类型,将数据和与这些数据相关的操作(函数)封装在一起,使类中的数据得到很好的“保护”。 类的声明 类的声明格式: class 类名 { private : 成员数据; 成员函数; public : protected: }; 类名 class Student { private : char Name[20]; float Math; public : float average; void SetName(char *name); void SetMath(float math); float GetAverage(void); }; 关键字 私有 公有 保护 分号不能少 类也是一种数据类型,将数据和与这些数据相关的操作(函数)封装在一起,使类中的数据得到很好的“保护”。
priviate(默认)限定的成员称为私有成员,对私有成员限定在该类的内部使用。类就相当于私有成员的作用域。 私有的成员数据只允许该类中的成员函数使用, 私有的成员函数只能在该类内被调用 public限定的成员称为公有成员,公有成员的数据或函数不受类的限制,可以在类内或类外自由使用。
protected限定的成员称为保护成员,只允许在类内及该类的派生类中使用保护的数据或函数。 保护成员的作用域是该类及该类的派生类。 私有成员 公有成员 保护成员 类内函数 可以调用 类外函数 不可调用
每一个限制词(private等)在类体中可使用多次。一旦使用了限制词,该限制词一直有效,直到下一个限制词开始为止。 成员函数与成员数据的定义不分先后。
class Student { char Name[20]; float Math; float Chinese; public : float average; void SetName(char *name); void SetMath(float math); void SetChinese(float ch); float GetAverage(void); }; 均为私有权限 均为公有权限
在类外不能直接使用 x 或 y ,必须通过Setxy()给 x 或 y 赋值,通过Print()输出 x 或 y 。 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() Print() 私有数据 公有函数 在类外不能直接使用 x 或 y ,必须通过Setxy()给 x 或 y 赋值,通过Print()输出 x 或 y 。
在C++语言中,结构体类型只是类的一个特例。 结构体类型与类的唯一的区别在于: 类成员的缺省的存取权限是私有的; 结构体类型成员的缺省的存取权限是公有的。
对象的定义 在定义类时,只是定义了一种数据类型,即说明程序中可能会出现该类型的数据,并不为类分配存储空间。 只有在定义了属于类的变量后,系统才会为类的变量分配空间。 类的变量称为对象。 对象是类的实例,定义对象之前,一定要先声明该对象的类。
不同对象占据内存中的不同区域,它们所保存的数据各不相同,但对成员数据进行操作的成员函数的程序代码均是一样的。 对象的定义格式: 存储类型 类名 对象1,对象2,......; Student st1,st2; 类名 对象名 在建立对象时,只为对象分配用于保存数据成员的内存空间,而成员函数的代码为该类的每一个对象所共享。
对象的定义方法同结构体定义变量的方法一样。 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 a1,a2; 定义全局对象 void main(void) { A a3,a4; } 定义局部对象
类的成员函数 可以在类体中定义函数体。 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; } 在类体外定义成员函数
void A::Setxy(float a,float b) { x=a; y=b; } 在类体外定义成员函数的格式: <type> < class_name > :: < func_name > (<参数表>) { ...... //函数体 } 类名 函数类型 函数名 形参列表 void A::Setxy(float a,float b) { x=a; y=b; } 函数体
内联成员函数 定义一个类时,可以在类中直接定义函数体。这时简单的成员函数在编译时自动作为内联函数来实现的。 我们也可以在类体外定义类的内联成员函数,在类体内说明函数,在类体外定义时,在成员函数的定义前面加上关键字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; }
一个对象的成员就是该对象的类所定义的成员,有成员数据和成员函数。 对象成员的使用 一个对象的成员就是该对象的类所定义的成员,有成员数据和成员函数。 通过对象名和成员运算符访问对象中的成员 A a; a. 通过指向对象的指针访问对象中的成员 A a; A * pa = &a; pa-> 通过对象的引用来访问对象中的成员 A &b = a; b.
输出: 2 5 x y m n Setxy() Print() x y m n Setxy() Print() a1 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(); } Print() Setxy() a2 n m y x 输出: 2 5
用成员选择运算符“.”只能访问对象的公有成员,而不能访问对象的私有成员或保护成员。 要访问对象的私有的数据成员,只能通过对象的公有成员函数来获取。
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 Time { public: //数据成员是公用的 int hour; int minute; }; Time t,*p; //定义对象t和指针变量p p=&t; //使p指向对象t cout<<p->hour; //输出p指向的对象中的成员hour 在p指向t的前提下,p->hour,(*p).hour和t.hour 三者等价
通过对象的引用变量来访问对象中的成员
类库(不是函数库) C++编译系统提供的标准类库 用户自定义的类库:用户根据自己的需要做成的 类库,提供给自己和自己授权的人使用 在程序开发工作中,类库是很有用的,它可以 减少用户自己对类和成员函数进行定义的工作量。 类库包括两个组成部分: (1)类声明头文件; (2)已经过编译的成员函数的定义,它是目标文件。 用户只需把类库装入到自己的C++编译系统能找到子目 录下,并在程序中用#include命令将有关的类声明头文 件包含到程序中,就可以使用这些类和其中的成员函 数,顺利地运行程序。
举例 **Person.h** #pragma once #include <string> class Person { private: std::string firstName; std::string lastName; int age; public: Person(); Person(std::string fName, std::string lName); Person(std::string fName, std::string lName, int age); ~Person(); void SayHello(); };
Person.cpp