页数:83页文件大小:601.0 KB来源: 计算机语言与程序设计ppt课件
本课件系统讲授面向对象程序设计:先回顾结构化程序设计方法并形成对比,再讲面向对象的基本概念、对象与类的关系、封装继承与多态三大特征,随后介绍C++语言在C基础上的扩展、类的声明与成员函数实现、构造函数与析构函数,最后深入继承与多态,用虚函数说明运行时的动态绑定,并以猴子选大王的C++示例收尾。
本页为第十五讲 面向对象程序设计与 C++ 封面。
内容
一、程序设计方法概述
程序设计方法
早期的程序设计方法追求程序的高效率,编程过份依赖技巧,而不注重所编写程序的结构,也就是没有固定程序设计方法的时期。程序的可读性、可重用性都很差。其中一个典型问题是频繁使用 goto 语句。虽然这种方法存在很多问题,但对于单人完成较为简单的任务,事实上还是经常被采用的。
结构化方法出现在 70 年代中期,我们可以这样理解它:结构化程序设计方法是从程序要实现的功能的角度出发的。一般按照自顶向下、逐步求精的方式,将程序要完成的功能逐级划分成许多小的功能模块,象搭积木一样搭起来。这些小的功能模块最终都可以转化成三种基本控制结构的组合。所谓的功能可以理解为对数据的操作。在程序实现中,特定的功能或功能模块一般用函数来实现,它们要对特定的数据进行操作。
结构化设计方法的特点
本页给出三种基本控制结构的框图:顺序结构为语句 1、语句 2、语句 3 依次执行;分支结构按条件选择语句 1 或语句 2;循环结构按条件重复执行语句 1、语句 2。
例:从键盘输入一个学生的信息(包括姓名、年龄、性别、学号等)和一个老师的信息(包括姓名、年龄、性别、是否授课等),然后将信息输出到屏幕。
分析:根据需求(题目要求),我们可以把问题划分为两个功能模块,一个是输入模块,一个是输出模块,做完了输入模块,再做输出模块。再具体考虑每个模块如何实现(逐步求精)。我们用 C 语言来写,参看下面的代码:
// …… void main(){ char strStudentName[20]; int nStudentAge; char cStudentSex; int nStudentNumber; char strTeacherName[20]; int nTeacherAge; char cTeacherSex; int nIsTeaching; GetStudentInfo(…); GetTeacherInfo(…); PrintStudentInfo(…); PrintTeacherInfo(…);}说明:源文件该页输出老师信息处原文写作 PrintStudentInfo(…),与前文“输出老师信息”的注释不符,整理时按语义修正为 PrintTeacherInfo(…)。
上面的例子中,我们可以进一步将属于学生和老师的变量放入结构中。这样可以在一定程度上完成对数据的封装。但在结构化程序设计中,数据与对其进行操作的函数仍是分离的。
//声明学生结构Student struct Student { char strStudentName[20]; int nStudentAge; char cStudentSex; int nStudentNumber;};struct Teacher { char strTeacherName[20]; int nTeacherAge; char cTeacherSex; int nIsTeaching;};本页为数据与函数关系的示意图,图中左侧为描述学生、老师的数据,右侧为对这些数据进行操作的函数,两者彼此分离。
问题:函数用于完成一定的功能,它们都是针对特定的数据进行操作的。那么我们能不能以特定的数据为中心,将数据与对其进行操作的函数封装起来呢?
面向对象程序设计方法
简单地说,对象就是现实世界中的各种实体,包括人、地点和事物等。例如,桌子、椅子、教室、学生、老师、电话、汽车等等。一般都要从属性和行为两个方面来对它们加以描述。在这里,我们认为对象和对象的实例是同一个概念。
对象具有的一些特征称为属性,以一个人为例,他的姓名、年龄、身高、体重等可以作为他的属性。这些属性会有其对应的值,一般至少会有一项区别于其它对象,它们在程序设计中对应的是一定的数据。为了达到目的,对象必须提供的功能(或必须提供的服务)称为对象的行为,在程序设计中对应一定的方法(函数)。
类描述了一组具有相同属性(数据元素)和相同行为(函数)的对象。类的数据成员是对对象属性的抽象,类的函数成员是对对象行为的抽象,而类本身就是对对象的抽象。
例:C++ 中类的声明——Student 类
class Student{ public: Student(); ~Student(); char* GetName(); int GetAge(); char GetSex(); int GetNumber(); bool SetName(char* n); bool SetAge(int age); bool SetSex(char* s); bool SetNumber(int num);protected: char m_strName[20]; int m_nAge; char m_cSex; int m_nNumber;};说明:源文件 char GetSex(); 的注释原文为“查询姓名”,按函数语义修正为“查询性别”。
例:C++ 中类的使用
…… Student A;A.SetName("张三");A.SetAge(20);……本页为类的构成示意图,图中给出成员函数(行为)与成员变量(属性)封装在同一个类之内。
总的来说:
二、面向对象程序设计方法
面向对象方法的主要特点
数据抽象:类是一组相似对象的抽象描述,它抽取了这些对象的共性组成了一个共同的概念。抽象数据类型(Abstract Data Type,ADT)是一组相似的类的抽象,而一个类又是 ADT 的具体实现。
封装是指软件的组成部分(模块、子程序、方法等)应该互相独立,或者隐藏设计的细节。在传统的方法中,封装通常局限于将功能和数据分开封装;而在面向对象方法中,封装将功能和数据同时装入对象中。参看 Student 类的例子。
例:C++ 中类的声明——Student 类(封装示例)
class Student{ public: Student(); ~Student(); char* GetName(); int GetAge(); char GetSex(); int GetNumber(); bool SetName(char* n); bool SetAge(int age); bool SetSex(char* s); bool SetNumber(int num);protected: char m_strName[20]; int m_nAge; char m_cSex; int m_nNumber;};继承:如果类与类之间有 is-a(是一种) 的关系,那么可以采用继承机制来表示。子类可以自动继承父类中的一些属性和行为,而不必再进行定义,从而实现了代码的复用。同时,继承也是产生新类的方法之一。
从上图可以看出,学生和教师都是人的一种,所以,学生类和教师类可以从人类继承而来,从而实现了代码的共享。
类的声明举例——People 类
class People{ public: People(); ~People(); char* GetName(); int GetAge(); …… bool SetName(char* n); bool SetAge(int age); …… private:protected: char m_strName[20]; int m_nAge; char m_cSex; …… };类的声明举例——Teacher 类
class Teacher: public People{ public: Teacher(); ~Teacher(); bool IsTeaching(); …… private: …… protected: bool m_bIsTeaching; …… };类的声明举例——Student 类
class Student : public People { public: Student(); ~Student(); int GetNumber(); bool SetNumber(int n); …… private: …… protected: int m_nNumber; …… };在程序中同一符号或名字在不同情况下具有不同解释的现象称为多态性(Polymorphism)。在面向对象程序设计语言中,由程序员设计的多态性有两种基本形式:编译时多态性和运行时多态性。许多程序设计语言都或多或少地支持多态性,但运行时多态性是面向对象程序设计语言的一大特点。
编译时多态性是指在程序编译阶段即可确定下来的多态性,主要通过使用重载(Overloading)机制获得,重载机制包括函数重载和运算符重载两大类。举一个 C++ 中的例子:
int Abs(int x); double Abs(double x); cout<<Abs(-4)<<endl;cout<<Abs(3.2)<<endl; 第35页:运行时多态性与动态绑定
运行时多态性是指必须等到程序动态运行时才可确定的多态性,主要通过继承结合动态绑定获得。动态绑定也称晚绑定,它也是面向对象的重要特点之一。动态绑定的使用可以提高程序的可用性和可扩展性。
第36页:面向对象方法的三种基本活动
八十年代末以来,由 Booch,Coad,Yourdon,OMT,Jacobson 等人提出的多种面向对象方法已经得到了广泛的应用。面向对象方法中的三种基本活动就是:
- 识别对象和类
- 描述对象和类之间的关系
- 通过描述类的功能定义对象的行为
第37页:科德方法的四个部件
科德(Coad)方法将对象模型分为下面四个部件,也就是将对象分为了四组:
- 问题域 PD(problem domain)
- 人机交互 HI(human interaction)
- 数据管理 DM(data management)
- 系统交互 SI(system interaction)
说明:源文件中“系统交互(SI)”原编号为 (2),与前面的 (2) 重复,按四项部件的顺序整理为 (4)。
第38页:问题域部件与人机交互部件
- 问题域部件(PD)通常最先考虑,因为用户往往最关心为自己的商业问题建模。问题域部件包含与需要建模的问题直接有关的对象。问题域部件的对象技术上呈现中型,它们几乎不了解或完全不了解人机交互、数据管理和系统交互部件的对象
- 人机交互部件(HI)包含为问题域对象和人们之间提供界面的对象。在对象模型中,人机交互部件的对象通常对应具体的窗口和报表
第39页:数据管理部件与系统交互部件
- 数据管理部件(DM)包含为问题域对象和数据库系统或文件管理系统之间提供界面的对象。在对象模型中,数据管理部件的对象通常对应某些需要保存及搜索的问题域对象
- 系统交互部件(SI)包含为问题域对象和其它系统或设备提供界面的对象。系统交互对象封装了通信协议,使得问题域对象不需要了解底层的实现细节
第40页:科德方法的四个活动
科德的面向对象方法包括以下四个活动:
- 确定系统的目标和特点
- 确定四种模型部件的对象和模式
- 建立对象责任
- 定义服务场景以完成特定服务责任
说明:源文件中第 4 项原编号为 (2),按四项活动的顺序整理为 (4)。
第41页:C++语言章节
三、C++ 语言
- C 与 C++ 的比较
- C++ 中的数据类型
- C++ 程序框架
- 类和对象:类的声明、实现、继承、多态性,并举例
第42页:C语言的优点
C 语言的优点:
- 与硬件无关,可移植性强
- 语言简洁,使用方便
- 丰富的运算符和数据类型
- 可直接访问内存地址
- 能进行位操作
- 目标代码质量高,运行效率高
第43页:C语言的弱点
C 语言的弱点:
- 检查机制弱,编译时不能发现编程错误
- 面向过程的语言,没有支持代码复用的机制
- 很难控制大规模程序的复杂性
第44页:C++语言的特点
C++ 语言
- 是 C 的超集,保持了与 C 的兼容性
- 继承了 C 语言高效性、灵活性的特点
- 扩充了对面向对象程序设计和高层次问题抽象方法的支持,是面向对象程序设计语言之一
- 完善了 C 语言的类型检查、代码重用、数据抽象机制,便于大规模软件开发
- 既反映了结构化程序设计方法,又反映了面向对象程序设计方法
第45页:一个简单的C++程序
一个简单的 C++ 程序的例子
#include <iostream.h>void main(){ cout << "Hello the world!" << endl; }cout << "输入的数据为" << a << endl;
第46页:C++中的数据类型
C++ 中的数据类型
- 基本数据类型:整型(int)、字符型(char)、浮点型(float)、双精度型(double)、布尔型(bool)、空类型(void)
- 构造数据类型:引申数据类型包括指针(*)、引用(&)、数组([]);结构化数据类型包括结构(struct)、联合(union)、枚举(enum)、类(class)
第47页:引用类型
引用 & 一般用于参数传递。& 可以认为是取地址。引用类型的变量并不真正创建新的对象,而是作为另一个对象的别名。例如:
void swap(int& a, int& b){ int temp; temp = a; a = b; b = temp; }
对形参 a,b 的操作等价于对实参的操作。
第48页:C++程序结构
C++ 程序结构:C++ 源代码一般都由若干类或函数组成。为了便于管理,一般把不同的类放在不同的文件中,对于类的声明和实现也分别放在对应的 .h(或 .hpp)和 .cpp 文件中。由于文件较多,所以为了便于管理,一般的集成开发工具都会提供工程(Project)管理功能来管理这些文件,对源文件进行编译和链接。
第49页:工程文件组织示意图
本页为工程文件组织的示意图,图中给出类的声明文件与实现文件在工程中的组织方式。
第50页:防止重复包含
为防止重复包含(多次包含同一头文件,造成重复定义)和嵌套包含(互相包含)造成的错误,应该在头文件里加上如下预编译命令。例如,对于 test1.h:
#ifndef __INCLUDE_TEST1_H__ #define __INCLUDE_TEST1_H__…… #endif
第51页:类的声明格式
class 类名 { public: 公有数据和函数 protected: 受保护数据和函数 private: 私有数据和函数 };
类的声明注意要有分号。
第52页:People类的声明
类的声明举例——People 类
class People{ public: People(); People(char* name, int age, char sex); ~People(); char* GetName(); int GetAge(); char GetSex(); bool SetName(char *n); bool SetAge(int age); bool SetSex(char sex);private:protected: char m_strName[20]; int m_nAge; char m_cSex;};
第53页:有关类的声明
有关类的声明
- 类名是有效的标识符,不能是关键字(保留字)
- 数据成员在声明时不能进行初始化
- 数据成员不能是正在声明的类,但可以用指针
- 访问控制 public、private、protected 可以以任意次序出现,并可重复多次
第54页:构造函数和析构函数
构造函数和析构函数
- 类的成员函数中有两个函数是特殊的,就是构造函数和析构函数,它们没有返回值,且一般不直接调用这些函数,而是在对象建立和撤销时分别被隐式调用的
- 构造函数的函数名要与类名相同,在创建对象时被调用。可以不定义构造函数,编译程序会自动生成一个缺省的构造函数
- 析构函数的函数名是在类名前加上一个“~”。同上,也可以不定义析构函数,编译程序会自动生成一个缺省的析构函数
第55页:构造函数与析构函数的任务
- 在类的构造函数中,一般要完成一些初始化的任务。例如将一些指针赋值为 NULL,给一些变量赋初值,或者为一些指针分配空间等。还可以利用函数的重载,声明并实现多种构造函数,以完成不同的初始化任务(参见 People 类)
- 在类的析构函数中,一般要进行一些收尾工作,如空间的释放。如对于为指针分配的空间,要加以释放等
第56页:类成员访问控制
类成员访问控制
- public:公有成员,可以被任何函数访问(即可以从类的外部进行访问)
- private:私有成员,只有类成员函数和友员类可以访问
- protected:保护成员,只有类成员函数、派生类、友员类可以访问
- 缺省方式为 private
第57页:People类的构造与析构实现
类的实现——People 类
People::People(){ memset(m_strName, 0, sizeof(m_strName)); m_nAge=0; m_cSex='0';}People::~People(){} 第58页:带参构造函数的实现
People::People(char* name, int age=0, char sex='0'){ if (name!=NULL) strcpy(m_strName,name); else memset(m_strName, 0, sizeof(m_strName)); if((age>=0) && (age<=200)) m_nAge=age; else m_nAge=0; switch (sex) { case 'M': m_cSex='M'; break; case 'F': m_cSex='F'; break; default: m_cSex='0'; } }
说明:源文件该处写作 m_nAge{ m_nAge=age; },语句存在粘连,整理时按 C++ 语法修正为 m_nAge=age;,语义不变。
第59页:查询成员函数的实现
//查询姓名的成员函数 char* People::GetName() { return m_strName;}int People::GetAge() { return m_nAge;}char People::GetSex() { return m_cSex;}// ……
第60页:People类的主函数
void main(){ People A; People B("Li Si"); People* C = new People("Zhang San", 20, 'M'); A.SetName("Wang Wu"); cout << "A's name:" << A.GetName() << endl; cout << "A's age:" << A.GetAge() << endl; cout << "A's sex:" << A.GetSex() << endl << endl; cout << "B's name:" << B.GetName() << endl; cout << "B's age:" << B.GetAge() << endl; cout << "B's sex:" << B.GetSex() << endl << endl; cout << "C's name:" << C->GetName() << endl; cout << "C's age:" << C->GetAge() << endl; cout << "C's sex:" << C->GetSex() << endl << endl; delete C;}
第61页:程序运行结果
程序运行结果
A's name:Wang Wu A's age:0 A's sex:0 B's name:Li Si B's age:0 B's sex:0 C's name:Zhang San C's age:20 C's sex:M
第62页:类对象的声明
//声明类People的对象A,调用没有参数的构造函数 People A;People B("Li Si");People* C = new People("Zhang San", 20, 'M'); 或: People *C; C = new People("Zhang San", 20, 'M');
类对象的声明。
第63页:对象的复制
People A("Zhang San"), B;B = A; 第64页:继承的概念
继承是面向对象程序设计的重要特点之一。它描述了类之间的 is-a 的关系。如果类 B 继承类 A,则称类 A 是类 B 的基类(或父类),类 B 是类 A 的派生类(或子类)。
第65页:Student类的继承声明
举例——Student 类
class Student : public People { public: Student(); ~Student(); int GetNumber(); bool SetNumber(int n);private: protected: int m_nNumber;};
类声明第一行后面的“: public People”指明了 Student 类继承自 People 类。public 是继承访问控制,用于指明继承成员在子类中的访问控制。这样,类 People 中的一些成员就会被 Student 类自动继承了。
第66页:继承成员的访问控制表
继承成员的访问控制(父类访问控制 × 继承访问控制) 父类访问控制 public 继承 protected 继承 private 继承 public public protected private protected protected protected private private 不可访问 不可访问 不可访问
说明:源文件该页表格文字与数字严重粘连,无法安全切分,上表按 C++ 语言标准的继承访问控制规则整理而成,供对照使用。
第67页:继承访问控制的要点
继承成员的访问控制
- 父类中的私有成员不能继承
- 继承访问控制为 public 时,继承父类的访问控制
- 继承访问控制不是 public 时,子类中继承成员的访问控制与继承访问控制相同
第68页:类型兼容性
类型兼容性:C++ 语言中,允许且仅允许公有派生类兼容基类。这就是说,子类的对象可以赋值给父类的对象,指向父类的指针可以指向子类。反之则不正确。例:
class Child:public Father Father* father;Child* child;//动态创建对象father = child;child = father; 第69页:多态性概述
多态性:如前所述,在程序中同一符号或名字在不同情况下具有不同解释的现象称为多态性。在面向对象程序设计语言中,由程序员设计的多态性有两种基本形式:编译时多态性和运行时多态性。运行时多态性是面向对象程序设计语言的一大特点。
第70页:编译时多态性
编译时多态性是指在程序编译阶段即可确定下来的多态性,主要通过使用重载机制获得,重载机制包括函数重载和运算符重载两大类。在声明函数原型时只要形式参数的个数或类型不同,就可以使用相同的函数名。只有返回值的不同同名函数,并不认为是不同的函数。运算符重载允许程序员重新定义 C++ 语言已有的运算符。
第71页:函数重载
函数重载
int Add(int a, int b);float Add (float a, float b);double Add (double a, double b);int Abs(int a);float Abs(float a);double Abs(double a); 第72页:运算符重载
运算符重载:运算符可以理解为函数,一元、二元运算符分别认为是具有一个、两个参数的函数。那么,对一个 n 维矢量类,我们可以重载“+”,“-”运算符,以实现矢量的加减运算。例:
class Vector: Vector operator+(const Vector& Vec);Vector operator-(const Vector& Vec);Vector operator-( );Vector operator=(const Vector& Vec); 第73页:运行时多态性
运行时多态性是指必须等到程序动态运行时才可确定的多态性,主要通过继承结合动态绑定获得。这与类的继承密切相关。因为存在类型的兼容性,所以有些函数只有在运行时才能确定是调用父类的还是子类的函数。在 C++ 中,使用虚函数(Virtual Functions)来实现。
第74页:虚成员函数
虚成员函数主要用于要在子类(派生类)中重新定义此函数。virtual 只需要在父类中定义,子类中对应的函数会默认为虚函数。子类中重新定义的函数必须与父类中的函数参数和返回值相同。不过,只有返回值不同,不会被认为与原函数不同。
第75页:虚函数示例的类声明
class A{ public: virtual ShowData();protected: int m_nData;}; class B : public A{ public: virtual ShowData();};
第76页:虚函数的主函数
void main() { A* a = new A(), *c; B* b = new B(); c = a; c->ShowData(); c = b; c->ShowData();}
如果 ShowData() 不是虚函数,则不管 c 指向父类还是子类的对象,所调用的都是父类的函数。
第77页:虚函数的限制
虚函数的限制
- 虚函数仅适用于有继承关系的类对象,所以只有类的成员函数才能说明为虚函数
- 静态成员函数不能是虚函数
- 内联(inline)函数不能是虚函数
- 构造函数不能是虚函数
- 析构函数可以是虚函数
第78页:参考资料
参考资料
- 《面向对象系统分析与设计》Ronald J. Norman 著,周之英等译,2000
- 《面向对象程序设计基础》,李师贤,李文军,周晓聪编著,高等教育出版社,1998
- C++ 语言和面向对象程序设计(第二版),宛延闿
- C++ 讲义,徐玉华、殷人昆老师提供
- http://www.cbi.pku.edu.cn/Doc/teach/cpp/
- 面向对象方法综述,陈小群
第79页:猴子选大王C++举例
举例:猴子选大王,包含以下文件:
- Monking.cpp:主程序
- Mongkey.h:Monkey 类的声明
- Mongkey.cpp:Monkey 类的实现
- Ring.h:Ring 类的声明
- Ring.cpp:Ring 类的实现
第80页:Monkey.h类的声明
//***************************** #include <iostream.h> #include "Monkey.h" void main(){ Monkey monkey; monkey.Initial(); monkey.Getking();} 第83页:第十五讲结束页
本页为第十五讲 面向对象程序设计与 C++ 的结束页,本讲内容至此结束。
下载地址:{{downurl}}
{{{a}}}
{{#b}}{{{b}}}
{{/b}} {{/title}} {{#nolog}}登陆注册
{{/nolog}} {{#djsx}}点我刷新
{{/djsx}} {{#gocz}} {{/gocz}} {{#ffqr}} {{/ffqr}}