1、第七章 类与对象,3,教学目标,(1) 理解以类与对象为核心的面向对象程序设计的基本思想,包括封装、继承和多态性的概念及其在面向对象程序中的体现; (2) 掌握类的定义方法; (3) 掌握类成员的访问权限控制; (4) 掌握对象的创建与使用方法; (5) 了解构造函数与析构函数的定义与使用。 (6) 了解类的继承与派生、运算符重载、以及模板等概念。,4,基本内容,7.1 面向对象程序设计概述 7.2 类与对象 7.3 继承,5,7.1 面向对象程序设计概述,两种程序设计思想:面向过程的程序设计(Structure Programming) 以功能为中心,采用函数来描述操作(动词)面向对象程序设
2、计(Object Oriented Programming) 以数据为中心,采用对象来描述内部属性和操作方法(名词),6,对象和面向对象,客观世界实体 例:人类 静态特征(属性attribute) 姓名、性别、年龄、班级(/单位)、身高等 动态特征(行为behavior) 吃饭、睡觉、说话、走路、上课(/上班)等 例:汽车 静态特征(属性attribute) 品牌、生产厂、型号、颜色等 动态特征(行为behavior) 行驶、转弯、鸣笛、刹车等,7,面向对象的基本特征,抽象:数据抽象+代码抽象封装:过程和数据封藏起来继承:基类和派生类多态性:虚函数、函数重载,8,OOP的优点,开发时间短,效率
3、高,可靠性高,所开发的程序更强壮重用,共享,可维护性,精简适合于大程序长时间的开发工作,9,7.2 类与对象,7.2.1 类的定义 7.2.2 成员函数 7.2.3 对象的使用 7.2.4 构造函数与析构函数,10,类(Class) -数据类型,类是具有相同属性和相同的方法的对象的集合,它是一种既包含数据又包含函数的抽象数据类型。 类的构成 数据成员(Data Member),即类的属性; 成员函数(Member Function),即类的方法;,11,7.2.1 类的定义,class 类名 private: /私有成员私有的数据成员和成员函数;protected: /保护成员保护的数据成员和
4、成员函数;public: /公有成员公有的数据成员和成员函数; ;,12,访问控制权限,私有部分(private):数据成员和成员函数只能在该类的范围内被本类的成员函数访问体现类的封装性 保护部分(protected):与私有成员的性质相似 公有部分(public):成员既可以被本类的成员函数访问,也在类外被该类的对象访问对外界的接口,13,例:定义一个狗类(Dog),class Dog private:char name20; / 狗的名字int age; / 狗的年龄public:void set(char *n, int a); / 设置数据成员值的函数void display(); /
5、 输出数据成员值的函数void speak(); / 发出狗叫声的函数 ;,14,7.2.2 成员函数,成员函数的一般形式:函数类型 类名 : 函数名 ( 参数表 ) 函数体 ,15,类的作用域,一个类的所有数据成员和成员函数都在该类的作用域内(包括在类声明外部定义的成员函数)类中的每个成员都可以直接访问本类中的其他任何成员。如果在类作用域之外对该类的数据成员和成员函数进行访问,则要受到一定的限制,有时甚至是不允许的。,16,例:Dog类成员函数的定义,void Dog:set(char *n, int a) strcpy(name,n);age = a; void Dog:display()
6、 coutnametageendl; void Dog: speak() cout“Arf!Arf!“endl; ,/ Dog类较完整的定义 class Dog private:char name20;int age;public:void set(char *n, int a);void display();void speak(); ; void Dog:set(char *n, int a) strcpy(name,n);age = a; void Dog:display() coutnametageendl; void Dog: speak() cout“Arf!Arf!“endl;
7、,/ 当成员函数比较简单时,可在类定义中直接给出函数体 class Dog private:char name20;int age;public:void set(char *n, int a)strcpy(name,n);age = a;void display()coutnametageendl;void speak()cout“Arf!Arf!“endl; ;,19,7.2.3 对象的使用,对象是类的实例,先声明后使用说明对象的一般形式(类的实例化):类名 对象名表;对类成员的访问. 运算符- 运算符,20,例7-1: Dog类和对象,int main() char name020;in
8、t age0;Dog dog1, dog2;cout name0 age0;dog1.set (name0, age0);cout “第一条狗:“ endl;dog1.display();dog1.speak();dog2.set (“LaiFu“, 2);cout “第二条狗:“ endl;dog2.display ();dog2.speak();dog1=dog2; / 同类对象之间可以互相赋值cout “对象赋值后的第一条狗:“ endl;dog1.display();dog1.speak();return 0; ,21,对象的使用,成员函数可直接访问本类中定义的数据成员(公有和私有)类
9、的作用域内 类的对象访问其公有数据成员或成员函数需使用运算符“” 禁止直接访问一个对象中的私有/保护成员 同类对象之间可以整体赋值 对象用作函数的参数时属于赋值调用;函数可以返回一个对象。,22,7.2.4 构造函数与析构函数,构造函数(Constructor)是类的一个特殊成员函数,定义了创建对象的方法,提供了初始化对象的一种简便手段。与构造函数相对应,析构函数(Destructor)用于撤消一个对象,完成释放对象成员所占存储空间的工作。,23,构造函数(Constructor),格式: 类名 : 类名 (参数表) 函数体 注意事项: 构造函数必须与类名相同 构造函数没有返回值 其功能是对对
10、象进行初始化,一般由一系列赋值语句构成 由系统自动调用,24,析构函数(Destructor),格式为:类名:类名(); 注意事项: 函数名与类名相同,只是在前边加“”符号 析构函数不得返回任何值 析构函数不得带有任何参数 其功能是释放对象成员所占用的存储,一般由一系列的delete组成 由系统自动调用,25,例7-2: 使用构造函数和析构函数的Dog类,class Dog char name20;int age; public:Dog() / 构造函数1 strcpy(name, “XXX“);age = 0;Dog(char *n, int a) / 构造函数2 strcpy(name,
11、n);age = a;Dog() / 析构函数 cout“析构函数被调用“endl;void display() coutnametageendl; void speak() cout“Arf!Arf!“endl; ;,26,7.3 继承,7.3.1 派生类的定义 7.3.2 派生类的继承方式和访问属性 7.3.3 派生类的构造函数和析构函数,27,生物类的层次关系,28,7.3.1 派生类的定义,派生类的定义格式为:class 派生类名:继承方式 基类名1,继承方式 基类名2,继承方式 基类名n 派生类增加的成员声明; ;,29,/人员类定义 class Person protected:c
12、har Name10; /姓名char Sex; /性别 public:void ShowMe() /显示数据成员cout Namet Sexn; ; /雇员类定义 class Employee : public Person char Dept20; /工作部门 public:void ShowMe() /显示数据成员cout Namet Sext Dept n; ;,单继承的例子,30,/飞机类 class Plane char propeller; /螺旋桨 public:void flying(); /飞行方法 ; /船类 class Boat char helm; /舵 public
13、:void floating(); /飘浮方法 ; /水上飞机类 class Seaplane : public Plane, public Boat ;,多继承的例子,31,7.3.2 派生类的继承方式和访问属性,私有继承private公有继承public 保护继承protected,32,派生类的定义格式,class 派生类名:private | protected | public 基类名 private:protected:public: ;系统的默认值是私有继承,33,公有继承(public),以公有继承方式创建的派生类对基类各种成员访问权限如下 :基类公有成员相当于派生类的公有成员
14、,即派生类可以象访问自身公有成员一样访问从基类继承的公有成员。 基类保护成员相当于派生类的保护成员,即派生类可以象访问自身的保护成员一样,访问基类的保护成员。 对于基类的私有成员,派生类无法直接访问。,34,私有继承 (private),派生类对基类各种成员访问权限如下 :基类公有成员和保护成员都相当于派生类的私有成员,派生类只能通过自身的函数成员访问它们; 对于基类的私有成员,无论派生类内部成员或派生类使用者都无法直接访问。,35,保护继承(protected),派生类对基类各种成员访问权限如下 :基类的公有成员和保护成员都相当于派生类的保护成员,派生类可以通过自身的成员函数或其子类的成员函
15、数访问它们对于基类的私有成员,无论派生类内部成员或派生类使用者都无法直接访问,36,基类成员在派生类中的访问属性,/ 例7-3:派生类的定义及使用 #include #include using namespace std; class Person /人员类定义 char Name10; /姓名int Age; /年龄 public:void Set(char *name, int age) /设置数据成员 strcpy(Name, name);Age = age;void Display() coutName“t“Age“t“endl; ; class Student : public P
16、erson /学生类定义(公有继承) int Number; / 学号char ClassName10; / 班级 public:void SetStu(char *classname, int number, char *name, int age) strcpy(ClassName, classname); Number = number; Set(name, age); /派生类成员函数直接使用基类的公有成员void DisplayStu() cout Number t ClassName t; Display(); /派生类成员函数直接使用基类的公有成员 ; int main() St
17、udent stu1;stu1.SetStu(“计算机21“, 95071007, “张三“, 19);stu1.Display(); /派生类对象直接使用基类的公有成员stu1.DisplayStu();return 0; ,38,7.3.3 派生类的构造函数和析构函数,派生类构造函数的一般形式为:派生类名:派生类名(参数总表):基类名1(参数表1), ,基类名n(参数表n),内嵌对象名1(对象参数表1), ,内嵌对象名m(对象参数表m)派生类新增加成员的初始化;,39,派生类构造函数的执行次序,首先,调用基类构造函数,调用顺序按照它们被继承时声明的基类名顺序执行。其次,调用内嵌对象构造函数
18、,调用次序按各个对象在派生类内声明的顺序。最后,执行派生类构造函数体中的内容。,40,析构函数,派生类与基类的析构函数没有什么联系,彼此独立,派生类或基类的析构函数只作各自类对象消亡前的善后工作。派生类析构函数执行过程恰与构造函数执行过程相反。 首先执行派生类析构函数 然后执行内嵌对象的析构函数 最后执行基类析构函数,/ 例7-4:派生类构造函数和析构函数 #include #include using namespace std; class Person /人员类定义 char Name10; int Age; public:Person(char* name,int age) /构造函数
19、 strcpy(Name, name);Age = age;cout“Person类的构造函数被调用“endl; Person() /析构函数 cout“Person类的析构函数被调用“endl;void Display() coutName“t“Age“t“endl; ; class Student : public Person /学生类定义(公有继承) int Number; char ClassName10; public:Student(char *classname, int number, char *name, int age) : Person(name, age) strc
20、py(ClassName, classname);Number = number; cout“Student类的构造函数被调用 “endl;Student() cout“Student类的析构函数被调用“endl; void DisplayStu() cout Number t ClassName t; Display(); ; int main() Student stu1(“计算机21“, 95071007, “张三“, 19);stu1.DisplayStu();return 0; ,42,扩展阅读,7.4 多态 7.4.1 运算符重载 7.4.2 虚函数 7.5 类模板,43,多态性是
21、指某类的对象在接受同样的消息时,所做出的响应不同。“接受同样的消息”指调用名称相同的成员函数, “所做出的响应不同”指函数实现的功能不同。 实现“一种接口,多种方法”的技术,7.4 多态,44,多态性的形式,编译时多态性 编译器对源程序进行编译时就可以确定所调用的是哪一个函数,编译时多态性通过重载来实现; 函数重载 运算符重载 运行时多态性 在程序运行过程中根据具体情况来确定调用的是哪一个函数; 虚函数,45,函数重载:一名多用运算符重载:已有运算符赋予多重含义 C+系统默认重载 “+” “-”等适用于int, double等多种类型 位运算“”和“”用作输入输出运算符,7.4.1 运算符重载
22、,46,运算符重载的定义,形式: 函数返回值类型 类名: operator 运算符 (参数表) 运算符重载实际也是一种函数重载 函数 operator 运算符重载了运算符,/例7-5: 重载复数类的运算符 class Complex double real, imag; public:Complex(double r = 0, double i = 0) / 带默认参数值的构造函数 real=r; imag=i; double Real() return real; / 取复数的实部double Imag() return imag; / 取复数的虚部Complex operator +(Co
23、mplex ,48,7.4.2 虚函数,定义: 在某基类中声明为virtual并在一个或多个派生类中被重新定义的成员函数 语法: virtual 函数返回类型 函数名(参数表) 函数体 用途: 实现多态性,通过指向派生类的基类指针,访问派生类中同名覆盖成员函数,49,例 7-6: 使用虚函数实现多态性,#include using namespace std; class Animal /基类:动物类 public:virtual void Speak() cout“How does a Animal speak?“endl; ; class Cat : public Animal /派生类:
24、猫类 public:virtual void Speak() cout“miao!miao!“endl; ; class Dog : public Animal /派生类:狗类 public:virtual void Speak() cout“wang!wang!“endl; ;,50,虚函数的使用限制,应通过指针或引用调用虚函数,而不要以对象名调用虚函数说明语句;在派生类中重定义的基类虚函数仍为虚函数,同时可以省略virtual关键字; 在派生类重新定义时,必须与基类中的原型完全相同;否则会成为一般的重载;不能定义虚构造函数,可以定义虚析构函数。,51,抽象类,纯虚函数 virtual 返回
25、类型 函数名(参数表)=0 具体实现只能在派生类中完成抽象类 至少包含一个纯虚函数的类 抽象类为其所有子类提供了统一的操作界面,使其派生类具有一系列统一的方法。,52,抽象类的使用要求,抽象类不能实例化 抽象类只作为基类被继承 可以定义指向抽象类的指针或引用,这个指针或引用必然指向派生类对象。从而实现多态性。,53,7.5 类模板,与函数模板相似,程序中可以通过高度抽象首先定义一个类模板,然后通过使用不同的实参生成不同的类。 类是对问题空间的抽象,而类模板则是对类的抽象,即更高层次上的抽象。 类模板的定义方法为: template class 类名 ;,/ 例7-7 类模板的使用 #inclu
26、de using namespace std; template class AnyType T x; public:AnyType(T a)x=a;T GetX()return x; ; int main() AnyType i (5);AnyType d (3.14);AnyType c (a);cout “整型类:“ i.GetX() endl;cout “双精度类:“ d.GetX() endl;cout “字符类:“ c.GetX() endl;return 0; ,55,上机指导,7.6 使用ClassView标签,56,应用举例,例7-8 设计一个时间类,要求能够设置时间、进行时间的加减运算,并以一定格式显示时间。例7-9 编写教师信息管理程序,实现显示教师信息、增加教师信息、删除教师信息等功能。,57,学好程序设计语言的唯一途径是你的编程能力与你在计算机上投入的时间成,结 束 语,上机练习,正比,