14.12 向上类型转换

在本章的前面,我们已经看到了由ifstream派生而来的类的对象如何有ifstream对象的所有特性和行为。在FName2.cpp中,任何ifstream成员函数应当能被FName2对象调用。

继承的最重要的方面不是它为新类提供了成员函数,而是它是基类与新类之间的关系,这种关系可被描述为:“新类属于原有类的类型”。

这个描述不仅仅是一种想象的解释继承的方法—它直接由编译器支持。举个例子来说,考虑称为Instrument的基类(它表示乐器)和派生类Wind(管乐器)。因为继承意味着在基类中的所有函数在派生类中也是可行的,可以发送给基类的消息也可以发送给这个派生类。所以,如果Instrument类有play()成员函数,那么Wind也有。这意味着,可以确切地说,Wind对象也就是Instrument类型的一个对象。下面的例子表明编译器是如何支持这个概念的。

14.12 向上类型转换 - 图1

在这个例子中,有趣的是tune()函数,它接受一个Instrument类型的引用。然而,在main()中,在tune()函数的调用中却被传递了一个Wind参数。我们可能会感到奇怪,C++对于类型检查应该是非常严格的,然而接受一个类型的函数为什么会这么容易地接受另一个类型。直到人们认识到Wind对象也是一个Instrument对象,这里tune()函数对于Instrument的所有调用对于Wind也都是可调用的(这是由继承所保证的)。在tune()中,这些代码对Instrument和从Instrument派生来的任何类型都有效,这种将Wind的引用或指针转变成Instrument引用或指针的活动被称为向上类型转换(upcasting)。

14.12.1 为什么要“向上类型转换”

这个术语的引入是有其历史原因的,而且它也与类继承图的传统画法有关:在顶部是根,向下生长(当然我们可以用任何我们认为方便的方法画我们的图)。对于Instrument.cpp的继承图是

14.12 向上类型转换 - 图2

从派生类到基类的类型转换,在继承图上是上升的,所以它一般称为向上类型转换。向上类型转换总是安全的。因为是从更专门的类型到更一般的类型—对于这个类接口可能出现的惟一的事情是它失去成员函数,而不是获得它们。这就是编译器允许向上类型转换而不需要显式地说明或做其他标记的原因。