12.1 面向对象语言的概念
面向对象编程(OOP)语言是在传统命令式编程语言(如Pascal和C)的基础上发展起来的,它们通过引入类和对象等概念,增强了语言处理复杂数据结构的能力。这些概念不仅提高了代码的重用性,还改善了软件的设计和维护性。
12.1.1 对象和对象类
对象
对象是面向对象编程的核心。它是包含了数据(属性)和一系列操作这些数据的函数(方法)的封装体。对象的属性定义了它的状态,而方法定义了对象的行为。对象通过方法相互作用,而方法的激活(调用)通常表现为o.m
的形式,其中o
是对象,m
是方法。
对象类(类)
类是定义对象属性和方法的模板或蓝图。它规定了属于该类的所有对象共有的属性和方法。类的概念扩展了命令式语言中的类型概念,提供了一种机制,允许程序员定义复杂的数据类型及其操作。类的实例化产生对象,每个对象都有自己的属性值,但它们共享同一套方法的实现代码。
类作为模块单元
在面向对象编程中,类形成了基本的模块化单元,允许开发者以一种更高层次的抽象来思考和解决问题。例如,可以为圆形、矩形等不同类型的图形定义各自的类,每个类包含了描述该图形所需的属性和方法。这样的设计不仅提高了代码的模块性,还使得继承和多态等面向对象的高级特性成为可能。
类与类型
在面向对象语言中,“类”和“类型”通常被视为同义词。每个类定义了一种新的类型,类的实例(对象)就是该类型的具体化。这一概念使得面向对象语言能够支持丰富的数据抽象和类型安全性。
总结
面向对象语言通过对象和类的概念,提供了一种强大的方式来封装数据和操作数据的方法,从而使得软件开发更加直观和灵活。这种封装不仅增强了代码的可重用性和扩展性,还促进了更高效的编程实践,如继承、多态和抽象。面向对象编程已成为当代软件开发的主流范式之一。
12.1.2 继承
定义与作用
继承允许一个类(派生类)继承另一个类(基类)的属性和方法,并在此基础上添加或修改特性。这个机制使得派生类具备了基类的所有功能,并能够扩展新的功能或覆盖(overwrite)基类的某些方法以适应新的需求。
继承的好处
- 代码重用:通过继承,可以重用基类的代码,减少重复代码的编写。
- 增加抽象层次:继承促进了类库的结构化,通过不同级别的抽象,代码变得更加清晰和易于管理。
- 多态性:继承是实现多态性的关键,允许以统一的方式处理不同类的对象。
继承的实例
以图形类库为例,基类GraphicalObj
定义了所有图形对象共有的方法如translate
和scale
。派生类如ClosedGraphics
和PolyLine
继承了这些方法,并可以添加新方法如area
或重写继承的方法以实现特定的功能。
抽象类与动态绑定
- 抽象类:包含未定义方法的类,即没有提供所有方法实现的类。抽象类通常作为基类,指定派生类必须实现的方法。
- 动态绑定:指在运行时而非编译时决定调用哪个方法的机制。它允许以统一的方式调用不同类的同名方法,而实际执行的是对象运行时类型的方法。
编译器与动态绑定
为实现动态绑定,编译器必须生成代码,使得在运行时可以根据对象的实际类型调用适当的方法。这通常通过虚方法表(vtable)或类似机制实现,每个对象或类都有指向其动态绑定方法的指针。
总结
继承增强了面向对象语言的表达力,使得开发者可以构建灵活且易于扩展的系统。通过继承和多态性,面向对象语言支持了代码的高度重用和抽象,同时也提出了对编译器设计的特殊要求,特别是在支持动态绑定方面。面向对象编程的这些特性促进了软件开发的效率和软件产品的质量。
12.1.3 信息封装
封装的概念
信息封装是面向对象设计的基石,允许开发者隐藏类的内部实现细节,只暴露出必要的操作接口。这种机制有助于降低软件系统的复杂性,增强模块的独立性,提高代码的可维护性和可重用性。
私有与公共特征
- 私有(Private)特征:类的私有属性和方法只能被类的内部方法访问。这些特征对于类的外部是隐藏的,是类的实现细节。
- 公共(Public)特征:公共属性和方法定义了类的外部接口,可被任何其他对象或函数访问。公共方法通常提供了操作私有数据的安全途径。
实现封装的机制
不同的面向对象语言提供了不同的机制来实现信息封装,包括定义类成员的可见性关键字(如private
、public
、protected
)以及更细致的访问控制机制(如友元类friend
)。这些机制确保了对象的数据安全性和完整性。
封装的好处
- 数据隐藏:封装使得对象的状态不会被外部代码随意修改,防止了对象状态的不一致性。
- 接口抽象:通过公开方法而非直接访问数据,类可以在不影响外部代码的情况下更改内部实现。
- 减少耦合:封装有助于减少系统各部分之间的依赖关系,使各部分可以独立开发和测试。
小结
面向对象语言通过封装、继承和多态提供了一种强大的方式来组织和管理代码。封装特别是信息封装,为软件开发提供了一种有效的抽象机制,使得开发者可以在保持内部数据和实现细节隐私的同时,定义清晰的操作接口。这不仅提高了代码的安全性,也增强了软件的可维护性和扩展性。