9.1 章节简介
本章和下一章要讨论面向对象的程序设计的两个极其重要的功能——继承(inheritance)和多态性(polymorphism)。继承是软件复用的一种形式,实现这种形式的方法是从现有的类建立新类。新类继承了现有类的属性和行为,并且为了使新类具有自己所需的功能,新类还要对这些属性和行为予以修饰。
软件复用缩短了程序的开发时间,促使开发人员复用已经测试和调试好的高质量的软件,减少了系统投入使用后可能出现的问题。所有这些都是激动人心的。利用多态性可以编写出对现有的各种类和将要实现的类予以加工的程序。继承和多态性是处理复杂软件的一种很有效的技术。
在建立一个新类时,程序员可以让新类继承预定义基类(baseclass)的数据成员和成员函数,而不必重新编写新的数据成员和成员函数。这种新类称为派生类(derivedclass)。派生类本身也可能会成为未来派生类的基类。对于单一继承,派生类只有一个基类。对于多重继承,派生类常常是从多个基类派生出来的,这些基类之间可能毫无关系。单一继承比较简单,我们介绍几个例子,使读者能很快成为专家。多重继承更复杂,也更容易出错,因此我们只显示简单的例子,建议读者在进一步深造之后再利用这种功能。
派生类通常添加了其自身的数据成员和成员函数,因而通常比基类大得多。派生类比基类更具体,它代表了一组外延较小的对象。对于单一继承,派生类和基类有相同的起源。继承的真正魅力在于能够添加基类所没有的特点以及取代和改进从基类继承来的特点。
C++ 提供三种继承:public、protected 和 private。本章以介绍 public 为主,附带介绍另外两种。第15章将介绍如何用 private 继承代替复合。第三种形式是 protected 继承,是 C++ 中的新生事物,用得还不多。在 public 继承中,派生类的对象也是其基类的对象,但是反过来基类对象不是其派生类的对象。本章要利用“派生类对象是基类的对象”这一关系完成一些有趣的操作。例如,把各种不同但又通过继承而相关的对象连成基类对象的链表,它允许用通常的方法处理各种对象。在本章和下一章中就会看到,这是面向对象程序设计的一种重要的功能。
本章介绍了一种新的成员访问控制形式,即 protected 访问。派生类及其友元允许访问 protected 基类成员,而其他函数则不行。
开发软件系统的经验表明,软件系统中的大部分代码都是处理紧密相关的特殊情况。由于设计者和程序员的精力都集中于这些特殊情况,因而很难在这种系统中看到“大手笔”的程序作品。面向对象的程序设计提供了几种“见树木而知森林”的方法,有时把这个过程称为抽象。
如果一个程序中有多种密切相关的特殊情况,通常的做法是用 switch 语句来区分各种特殊情况,然后对每种情况提供处理逻辑。第10章将讲述如何通过继承和多态性用更简单的逻辑取代 switch 逻辑。
我们要区别“是一个对象 和 有一个对象”的差别(以下简称 是 关系和 有 关系)。是 关系是一种继承,在 是 关系中,派生类的对象也以作为基类的对象处理。而 有 关系是一种复合(见图 7.4),在这种关系中,一个类的对象拥有作为其成员的其他类的对象。
派生类不能访问其基类的 private 成员,否则会破坏基类的封装性。但是,派生类能够访问基类的 public 成员和 protected 成员。基类中不应该让派生类通过继承而访问的成员要在基类中声明为 privale。派生类只能通过基类public和protected接口提供的访问函数访问基类的 private 成员。
继承存在的一个问题是派生类会继承它无需拥有或者不应该拥有的基类public成员函数。基类中不适合于派生类的成员可以在派生类中重新加以定义。有些情况下,不适合用 public 继承。
继承所最具有吸引力的特点是新类可以从现有的类库中继承。项目开发者可以开发出自己的类库,也可以利用已广为使用的类库。基于这种观点,将来有一天,软件也可以像当今的硬件一样用标准的可复用组件进行构造。未来需要功能更强的软件,软件的这种开发方式正可以迎接这种挑战。