(public) inheritance 这个表面上简单易懂的观念,一旦被近距离审阅,就会被证实是由两个相互独立的部分组成的:inheritance of function interfaces(函数接口的继续)和 inheritance of function implementations(函数实现的继续)。这两种 inheritance 之间的差异正好符合本书 IntrodUCtion 中论述的 function declarations(函数声明)和 function definitions(函数定义)之间的差异。 作为一个 class 的设计者,有的时候你想要 derived classes 只继续一个 member function 的 interface (declaration)。有的时候你想要 derived classes 既继续 interface(接口)也继续 implementation(实现),但你要答应它们替换他们继续到的 implementation。还有的时候你想要 derived classes 继续一个函数的 interface(接口)和 implementation(实现),而不答应它们替换任何东西。
为了更好地感觉这些选择之间的不同之处,考虑一个在图形应用程序中表示几何图形的 class hierarchy(类继续体系):
class Shape { public: virtual void draw() const = 0;
virtual void error(const std::string& msg);
int objectID() const;
... };
class Rectangle: public Shape { ... };
class Ellipse: public Shape { ... }; Shape 是一个 abstract class(抽象类),它的 pure virtual function(纯虚拟函数)表明了这一点。作为结果,客户不能创建 Shape class 的实例,只能创建从它继续的 classes 的实例。但是,Shape 对所有从它(公有)继续的类施加了非常强大的影响,因为
成员函数 interfaces are always inherited。就像 Item 32 解释的,public inheritance 意味着 is-a,所以对一个 base class 来说成立的任何东西,对于它的 derived classes 也必须成立。因此,假如一个函数适用于一个 class,它也一定适用于它的 derived classes。
Shape class 中声明了三个函数。第一个,draw,在一个明确的显示设备上画出当前对象。第二个,error,假如 member functions 需要报告一个错误,就调用它。第三个,objectID,返回当前对象的唯一整型标识符。每一个函数都用不同的方式声明:draw 是一个 pure virtual function(纯虚拟函数);error 是一个 simple (impure?) virtual function(简单虚拟函数);而 objectID 是一个 non-virtual function(非虚拟函数)。这些不同的声明暗示了什么呢?
考虑第一个 pure virtual function(纯虚拟函数)draw:
class Shape { public: virtual void draw() const = 0; ... }; pure virtual functions(纯虚拟函数)的两个最显著的特性是它们必须被任何继续它们的具体类重新声明,和抽象类中一般没有它们的定义。把这两个特性加在一起,你应该熟悉到。
声明一个 pure virtual function(纯虚拟函数)的目的是使 derived classes 继续一个函数 interface only。
这就使 Shape::draw function 具有了完整的意义,因为它要求所有的 Shape 对象必须能够画出来是合情合理的,但是 Shape class 本身不能为这个函数提供一个合乎情理的缺省的实现。例如,画一个椭圆的算法和画一个矩形的算法是非常不同的,Shape::draw 的声明告诉具体 derived classes 的设计者:“你必须提供一个 draw function,但是我对于你如何实现它不发表意见。”
顺便提一句,为一个 pure virtual function(纯虚拟函数)提供一个定义是有可能的。也就是说,你可以为 Shape::draw 提供一个实现,而 C++ 也不会抱怨什么,但是调用它的唯一方法是用 class name 限定修饰这个调用:
Shape *ps = new Shape; // error! Shape is abstract
Shape *ps1 = new Rectangle; // fine ps1->draw(); // calls Rectangle::draw
Shape *ps2 = new Ellipse; // fine ps2->draw(); // calls Ellipse::draw
class Airplane { public: virtual void fly(const Airport& destination);
...
};
void Airplane::fly(const Airport& destination) { default code for flying an airplane to the given destination }
class ModelA: public Airplane { ... };
class ModelB: public Airplane { ... };
为了表述所有的飞机必须支持一个 fly 函数,并为了“不同机型可能(在理论上)需要不同的对 fly 的实现”的事实,Airplane::fly 被声明为 virtual。然而,为了避免在 ModelA 和 ModelB classes 中些重复的代码,缺省的飞行行为由 Airplane::fly 的函数体提供,供 ModelA 和 ModelB 继续。
这是一个经典的 object-oriented 设计。因为两个 classes 共享一个通用特性(它们实现 fly 的方法),所以这个通用特性就被转移到一个 base class 之中,并由两个 classes 来继续这个特性。这个设计使得通用特性变得清楚明白,避免了代码重复,提升了未来的可扩展性,简化了长期的维护——因为 object-oriented 技术,所有这些东西都受到很高的追捧。XYZ 航空公司应该引以为荣。 现在,假设 XYZ 公司的财富增长了,决定引进一种新机型,Model C。Model C 在某些方面与 Model A 和 Model B 不同。非凡是,它的飞行不同。
XYZ 公司的程序员在 hierarchy(继续体系)中增加了 Model C 的 class,但是由于他们匆匆忙忙地让新的机型投入服务,他们忘记了重定义 fly function:
class ModelC: public Airplane {
... // no fly function is declared }; 于是,在他们的代码中,就出现了类似这样的东西:
Airport PDX(...); // PDX is the airport near my home
class ModelC: public Airplane { public: virtual void fly(const Airport& destination);
... };
void ModelC::fly(const Airport& destination) { code for flying a ModelC airplane to the given destination } 这一方案并非十分安全(程序员还是能通过 copy-and-paste 使他们自己陷入麻烦),但是它比最初的设计更加可靠。至于 Airplane::defaultFly,它是 protected(保护的)是因为它完全是 Airplane 和它的 derived classes(派生类)的实现细节。使用飞机的客户应该只在意它能飞,而不必管飞行是如何实现的。
void Airplane::fly(const Airport& destination) // an implementation of { // a pure virtual function default code for flying an airplane to the given destination }
class ModelA: public Airplane { public: virtual void fly(const Airport& destination) { Airplane::fly(destination); }
...
};
class ModelB: public Airplane { public: virtual void fly(const Airport& destination) { Airplane::fly(destination); }
...
};
class ModelC: public Airplane { public: virtual void fly(const Airport& destination);
...
};
void ModelC::fly(const Airport& destination) { code for flying a ModelC airplane to the given destination } 除了用 pure virtual function(纯虚拟函数)Airplane::fly 的函数体代替了独立函数 Airplane::defaultFly 之外,这是一个和前面的几乎完全相同的设计。本质上,fly 可以被拆成两个基本组件。它的 declaration(声明)指定了它的 interface(接口)(这是 derived classes(派生类)必须使用的),而它的 definition(定义)指定它的缺省行为(这是 derived classes(派生类)可以使用的,但只是在他们明确要求这一点时)。将 fly 和 defaultFly 合并,无论如何,你失去了给予这两个函数不同的保护层次的能力:原来是 protected 的代码(通过位于 defaultFly 中实现)现在成为 public(因为它位于 fly 中)。
class Shape { public: int objectID() const; ... }; 当一个 member function(成员函数)是 non-virtual(非虚拟的)时,不应该指望它在 derived classes(派生类)中的行为会有所不同。实际上,一个 non-virtual member function(非虚拟成员函数)指定了一个 invariant over specialization(超越非凡化的不变量),因为不论一个 derived class(派生类)变得多么非凡,它都把它看作是不答应变化的行为。如下所指除的,
声明一个 non-virtual function(非虚拟函数)的目的是 to have derived classes inherit a function interface as well as a mandatory implementation(使派生类既继续一个函数的接口,又继续一个强制的实现)。
对 pure virtual,simple virtual,和 non-virtual functions 的声明的不同答应你精确指定你需要 derived classes(派生类)继续什么东西。分别是 interface only(仅有接口),interface and a default implementation(接口和一个缺省的实现),和 interface and a mandatory implementation(接口和一个强制的实现)。因为这些不同的声明类型意味着根本不同的意义,当你声明你的 member functions(成员函数)时你必须在它们之间仔细地选择。假如你这样做了,你应该可以避免由缺乏经验的类设计者造成的两个最常见的错误。
第一个错误是声明所有的函数为 non-virtual(非虚拟)。这没有给 derived classes(派生类)的非凡化留出空间;non-virtual destructors(非虚拟析构函数)尤其有问题(参见 Item 7)。当然,完全有理由设计一个不作为 base class(基类)使用的类。在这种情况下,一套独享的 non-virtual member functions(非虚拟成员函数)是完全合理的。然而,更通常的情况下,这样的类既可能出于对 virtual(虚拟)和 non-virtual functions(非虚拟函数)之间区别的无知,也可能是对 virtual functions(虚拟函数)的性能成本毫无根据的担心的结果。事实是,几乎任何作为 base class(基类)使用的类都会有 virtual functions(虚拟函数)(还是参见 Item 7)。
另一个常见的错误声明所有的 member functions(成员函数)为 virtual(虚拟)。有时候这样做是正确的—— Item 31 的 Interface classes(接口类)可以作为证据。然而,它也可能是缺乏表明态度的决心的类设计者的标志。某些函数在 derived classes(派生类)中不应该被重定义,而且只要在这种情况下,你都应该通过将那些函数声明为 non-virtual(非虚拟)而明确地表达这一点。它不是为那些人服务的,他们假设假如他们只需花一些时间重定义你的所有函数,你的类就会被所有的人用来做所有的事情,假如你有一个 invariant over specialization(超越非凡化的不变量),请直说,不必害怕!
Things to Remember
·Inheritance of interface(接口继续)与 inheritance of implementation(实现继续)不同。在 public inheritance(公开继续)下,derived classes(派生类)总是继续 base class interfaces(基类接口)。
·Pure virtual functions(纯虚拟函数)指定 inheritance of interface only(仅有接口被继续)。
·Simple (impure) virtual functions(简单虚拟函数)指定 inheritance of interface(接口继续)加上 inheritance of a default implementation(缺省实现继续)。
·Non-virtual functions(非虚拟函数)指定 inheritance of interface(接口继续)加上 inheritance of a mandatory implementation(强制实现继续)。