第四章面向对象程序设计语言(编辑修改稿)内容摘要:

• 派生类的对象也看作是原有基类的对象,可以当作基类的对象使用(子类就是子类型,Liskov 代换原理, 2020 年图灵奖) • 类定义了对象的状态成分(数据成员)和一组相关操作(称为方法) • 方法调用总是针对某个对象进行的,将方法调用看作是给相应对象送一个消息,对象通过执行相应操作的方式对消息做出响应 • 对一个消息执行什么方法,由接收消息的对象的类型确定(根据该对象所属的类确定,这就是动态约束) • 计算,就是一组对象相互通讯的整体效果(对计算的另一种看法) OO 发展史 Smalltalk 还有一些独特的东西: • 变量采用引用模型,变量无类型,可以引用任何对象 • 语言里的一切都是对象: – 类也是对象,通过给类送 new 消息的方式要求创建类的实例 – 各种控制结构也是通过消息概念建立的 • 条件和逻辑循环是逻辑对象对特定消息的响应 • 枚举循环是整数对象对特定消息的响应 • 采用单根的类层次结构,以类 Object 作为所有类的超类 • 提供了块( block)的概念,作为控制结构的抽象机制 • 提出了容器的概念,开发了一个功能丰富的类库 • 与程序开发环境的紧密结合,并开发了 GUI 的基本概念和相关技术 Smalltalk 经过 7 76 发展到 Smalltalk 80,其概念和结构已臻成熟 OO 发展史 随着 Smalltalk 的成功,人们看到了 OO 的潜在威力 • 许多人开始研究如何把 OO 概念有效集成到常规语言里,提出了一批已有语言的 OO 扩充和许多新 OO 语言,如 ObjectPascal、 ObjectC 等 • 其中前期最成功并得到广泛应用的是 C++。 C++ 在 OO 概念的广泛接受和应用方面功不可没(具体理由见后面讨论)。 原因: – 在面向对象和高效程序之间取得较好的平衡 – OO 概念与常规语言的合理集成(在当时),支持数据抽象和面向对象的系统设计和程序设计,支持多泛型程序设计的结合,使与数据抽象和 OO 有关的许多新概念和新技术逐渐被实际软件工作者接受 • 随后是 OO 分析、 OO 设计和基于 OO 的软件开发等等 • 后来的其他成功语言包括 Java,微软提出 C,等等 • 出现了一些基于对象的脚本语言,如 Python, Ruby 等 • 现在,面向对象的开发已经成为一种主流的软件开发技术 面向对象的基本概念 面向对象的基本概念: • 在面向对象语言里定义数据抽象的基本定义机制是类,在一个类里可以定义数据成员和子程序成员(称为方法) • 封装是数据抽象和模块化的概念,与面向对象的概念并没有必然关系,但封装有助于更好发挥面向对象机制的作用 • (实在的)类被看作类型,可以用于生成(定义)实例,称为对象 • 已有的类可以作为定义新类的基础(基类、超类) – 可通过继承方式定义新类(子类,派生类),子类继承基类的行为 – 子类可以修改基类已经定义的行为,或者增加所需的新行为 • 把子类看作是子类型(通常),如果 D 是 B 的子类,那么: – 若 o 是 D 类型的对象,那么 o 也看作是 B 类型的对象 – 若变量 x 可以引用 B 类的对象,那么它也可以引用 D 类的对象 面向对象的基本概念 • 继承有两方面作用 1. 建立类型之间的层次关系 2. 重用基类的行为(代码和数据描述) 对于面向对象的行为而言,前一方面的功能更为重要 • 类中的子程序成员称为方法,方法需要通过具体的对象调用 • 在运行中调用方法时,实际调用的方法由作为调用出发点的那个对象的类型确定的(动态约束) – 动态约束是实现面向对象行为的关键 – 它为面向对象的机制提供了模块机制所不具有的弹性,使新的功能扩充可以比较自然地结合到已有的操作过程里 – 理解动态约束是理解面向对象的关键,动态约束的高效实现也是面向对象语言的实现的关键 面向对象的语言 虽然基本框架类似,不同面向对象语言之间也存在很大差异: 基本问题:采用什么样的对象模型 • 采用单根的类层次结构,还是任意的类层次结构。 • 提供那些继承方式。 – 例如 C++ 里提供了三种继承方式 • 允许多重继承。 还是只允许单继承。 • 是否提供丰富完善的访问控制机制。 • 采用基于继承的模型,还是基于指派的模型 • 基于类的模型,还是基于对象或原型的模型(如 JavaScript) • 对象本身的独立性(是否允许不属于任何一个类的对象) • 类本身是不是对象。 面向对象的语言 其他情况: • 是不是追求 “纯粹 ”的面向对象语言。 – Smalltalk 尽可能追求 “面向对象 ”理想,完全是重新设计的新语言 – Java 是接近理想的语言,但希望在形式上尽可能靠近常规语言 – C++ 设法在支持系统程序设计的过程性语言 C 上 “扩充 ”支持面向对象的机制,是一种多范型语言,支持多种程序设计方式 – 另外的一些语言(如 Ada)采用可能很不同的方式支持面向对象的程序设计,这里不准备详细介绍 • 采用值模型还是引用模型。 从本质上说,只有采用引用模型才能支持方法的动态约束,因此大多数面向对象语言采用引用模型 – C++ 采用值模型,可以创建静态对象或栈对象,但只有通过对象引用或指向对象的指针才能实现面向对象的动态约束行为 – Java 只能把 OO 功能应用于用户定义类型,基本类型采用值模型 面向对象的语言 • 是否允许静态对象或者堆栈对象(自动对象)。 多数面向对象语言只支持堆对象(通过动态存储分配创建的对象) – C++ 支持静态对象和自动对象,这种设计是希望尽可能借助于作用域规则来管理对象,避免依赖自动存储管理系统( GC) – 为在这种环境下编程,人们开发了许多利用自动对象的对象管理技术,如句柄对象,对象的 “创建即初始化 ”技术等 • 是否依赖自动废料收集( GC)。 由于 OO 程序常(显式或隐式地)创建和丢弃对象,对象之间常存在复杂的相互引用关系,由人来完成对象的管理和回收很困难。 大多数 OO 语言都依赖于自动存储回收系统 – GC 的引入将带来显著的性能损失,还会造成程序行为更多的不可预见性( GC 发生的时刻无法预见,其持续时间长短也无法预计) – Java 等许多语言都需要内置的自动废料收集系统 – C++ 是例外,其设计目标之一是尽可能避免对自动存储回收的依赖,以支持系统程序设计,提高效率,减少运行时间上的不确定性 面向对象的语言 • 是否所有方法都采用动态约束。 – 动态约束很重要,但调用时会带来一些额外的开销,如果需要调用的方法能够静态确定,采用静态约束有速度优势 – 大部分语言里的所有方法都采用动态约束 – C++ 和 Ada 提供静态约束(默认)和动态约束两种方式 • 一些脚本语言也支持面向对象的概念。 例如, – Ruby 是一个纯面向对象的脚本语言,其中的一切都是对象,全局环境看作一个匿名的大对象,全局环境里的函数看作这个对象的成员函数。 它还有另外一些独特性质 – JavaScript 支持一种基于对象和原型的面向对象模型。 其中没有类的概念,只有对象。 对象的行为继承通过原型获得 面向对象的语言 • 人们还提出了许多与面向对象机制有关的新想法和模型 • 许多新近的脚本语言提供了独特的面向对象机制:例如 – 基于对象原型(而不是类)的 OO 模型 – 在基于类的模型中允许基于对象的行为覆盖(可修改个别对象的行为) – 等等 • 总而言之,虽然今天面向对象的模型和语言已成为主流程序设计方法和主流程序语言,但是这类语言还远未成熟,还正在发展和研究中 – 许多语言的 OO 机制非常复杂,实际还不断提出一些新要求,使一些 OO 语言在发展中变得越来越复杂 – 如何提供一集足够强大,而且又简洁清晰的机制支持 OO 的概念和程序设计,还是这个领域中需要继续研究的问题 – OO 语言有关的理论研究还处在起步阶段,也是本领域不成熟的标志 OO 语言需要提供的新机制 • 定义类的语言机制(语言提供特殊的描述结构) • 描述或定义对象的机制 • 继承机制,描述类之间的继承关系。 可能定义继承关系的性质(如 C++ 里的类继承有 public、 protected 和 private 三种方式) • 与对象交互的机制(方法调用,消息传递) • 初始化新对象的机制(最好能自动进行,避免未初始化就使用的错误) • 类类型对象的动态转换机制(转换对一个具体对象的观点) • 控制类成员的访问权限的机制 • 对象销毁前的临终处理机制(最好能自动进行) • 对象的存储管理机制 可能还有其他机制: • 运行中判断对象的类属关系的机制、自反等等 面向对象语言的基本特征与实现 OO语言的发展 , 给出以下图示澄清了概念 : 封装 → 对象 (数据和操作 ) 局部性 、 可维护性 抽象 → + 类 概括描述 、 简单性 继承 → + 类体系 可重用性 多态 → 重载 、 类属 可扩充性 动态束定 → 面向对象 可交互性 基于对象的语言 基于类的语言 面向对象语言 Ada 83, Actor CLU Smalltalk、 Eiffel simula 67 C++, Ada 95, Java OO 程序 • 先看一点 OO 程序,复习一下基本 OO 程序的特征 • 这里看一段定义了几个类的 C++ 代码 定义 list_node 类,用于实现带头结点的双向循环链接表 每个结点里有一个域指向表头结点 OO 程序 定义 list_node 类,用于实现带头结点的双向循环链接表 每个结点里有一个域指向表头结点 OO 程序 定义一个 list类 注意: header 是个 list_node 定义的是有头结点的循环链表 OO 程序 通过继承定义 queue 类。 (只是作为示例) OO 程序 • 还可以定义通用的容器类: – 基本容器类没有具体数据域,不保存具体类型的元素,只实现容器操作,如:一些基本判断谓词,插入删除等等 – 通过继承实现存储具体类型的元素的具体容器 每个结点里有一个域指向表头结点 OO 程序 派生的 int 表结点类 使用这种 int 表的问题: 如果需要访问结点的数据内容,必须对取出的结点做强制 通用的表结点类 面向对象特征的实现 • 实现面向对象的语言,需要考虑它的几个标志性特征的实现 • 封装是一种静态机制,如 C++/Java 一类语言的各种访问控制机制也是静态的,都可以通过在符号表里记录信息,在编译中检查和处理 • 方法的实现与以模块为类型时局部子程序的实现一样。 由于每个方法调用有一个调用对象,因此方法需要一个隐含指针,被调用时指向调用对象,所有对该对象的数据成员的访问都通过这个指针和静态确定的偏移量进行 • 许多语言以这一指针作为一个伪变量,称为 this 或者 self,通过这种指针 • 访问调用对象,方式上与通过指针访问普通结构一样 • 实现面向对象语言的关键是两个问题: – 继承的实现,使派生类型的对象能当作基类的对象使用 – 动态约束的实现,能够从(作为变量的值或者被变量引用的)对象出发,找到这个对象所属的类里定义的方法 • 下面讨论实现的一些具体问题 封装 • 封装是一种静态机制,仅仅在程序加工阶段起作用,有关情况与模块机制类似,在加工后的程序里(可执行程序里)完全没有关于封装的信息 • 不同语言里对类的访问控制可能不同: – 作为 “开模块 ”(允许以特定方式任意访问类成员) – 作为 “闭模块 ”(凡是。
阅读剩余 0%
本站所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。 用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈本站将在三个工作日内改正。