C 箴言:接口继承和实现继承

2008-02-23 05:24:07来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

 (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

ps1->Shape::draw(); // calls Shape::draw

ps2->Shape::draw(); // calls Shape::draw

  除了帮助您在鸡尾酒会上给同行程式员留下印象外,这个特性通常没什么用处,然而,就像下面您将看到的,他能用来作为一个“为 simple (impure) virtual functions 提供一个 safer-than-usual 的实现”的机制。

  simple virtual functions 背后的故事和 pure virtuals 有一点不同。derived classes 照常还是继承函数的 interface,但是 simple virtual functions 提供了一个能够被 derived classes 替换的实现。假如您为此考虑一阵儿,您就会认识到

  声明一个 simple virtual function 的目的是让 derived classes 继承一个函数 interface as well as a default implementation。

  考虑 Shape::error 的情况:

class Shape {
public:
virtual void error(const std::string& msg);
...
};

  interface 需要每一个 class 必须支持一个在遭碰到错误时被调用的函数,但是每一个 class 能够自由地用他觉得合适的任何方法处理错误。假如一个 class 无需做什么特别的事情,他能够仅仅求助于 Shape class 中提供的错误处理的缺省版本。也就是说,Shape::error 的声明告诉 derived classes 的设计者:“您应该支持一个 error function,但假如您不想自己写,您能够求助 Shape class 中的缺省版本。”

  结果是:允许 simple virtual functions 既指定一个函数接口又指定一个缺省实现是危险的。来看一下为什么,考虑一个 XYZ 航空公司的飞机的 hierarchy(继承体系)。XYZ 只有两种飞机,Model A 和 Model B,他们都严格地按照同样的方法飞行。于是,XYZ 设计如下 hierarchy(继承体系):

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: C 程式中导出Word文档的简易方法

下一篇: C 跨平台游戏研发之ClanLib SDK