c++接口继承与实现继承

所谓接口继承,就是派生类只继承函数的接口,也就是声明;而实现继承,就是派生类同时继承函数的接口和实现。
我们都很清楚C++中有几个基本的概念,虚函数、纯虚函数、非虚函数。
虚函数:
虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。——MSDN
虚函数用来表现基类和派生类的成员函数之间的一种关系.
+`%K i;l"Z @2J-^5/0虚函数的定义在基类中进行,在需要定义为虚函数的成员函数的声明前冠以关键字 virtual. 航大博客-中国民航大学个人门户N-D&{'d t0/
基类中的某个成员函数被声明为虚函数后,此虚函数就可以在一个或多个派生类中被重新定义. 航大博客-中国民航大学个人门户;P,q4B%Q2g
在派生类中重新定义时,其函数原型,包括返回类型,函数名,参数个数,参数类型及参数的先后顺序,都必须与基类中的原型完全相同.
Alz&P­Z Y7m0虚函数是重载的一种表现形式,是一种动态的重载方式.

纯虚函数:
纯虚函数在基类中没有定义,它们被初始化为0。
任何用纯虚函数派生的类,都要自己提供该函数的具体实现。
定义纯虚函数
virtual void fun(void) = 0;
非虚函数:
一般成员函数,无virtual关键字修饰。
 
至于为什么要定义这些函数,我们可以将虚函数、纯虚函数和非虚函数的功能与接口继承与实现继承联系起来:
声明一个纯虚函数(pure virtual)的目的是为了让派生类只继承函数接口,也就是上面说的接口继承。
纯虚函数一般是在不方便具体实现此函数的情况下使用。也就是说基类无法为继承类规定一个统一的缺省操作,但继承类又必须含有这个函数接口,并对其分别实现。但是,在C++中,我们是可以为纯虚函数提供定义的,只不过这种定义对继承类来说没有特定的意义。因为继承类仍然要根据各自需要实现函数。
通俗说,纯虚函数就是要求其继承类必须含有该函数接口,并对其进行实现。是对继承类的一种接口实现要求,但并不提供缺省操作,各个继承类必须分别实现自己的操作。
声明非纯虚函数(impure virtual)的目的是让继承类继承该函数的接口和缺省实现。
与纯虚函数唯一的不同就是其为继承类提供了缺省操作,继承类可以不实现自己的操作而采用基类提供的默认操作。
声明非虚函数(non-virtual)的目的是为了令继承类继承函数接口及一份强制性实现。
相对于虚函数来说,非虚函数对继承类要求的更为严格,继承类不仅要继承函数接口,而且也要继承函数实现。也就是为继承类定义了一种行为。
 
总结:
纯虚函数:要求继承类必须含有某个接口,并对接口函数实现。
虚函数:继承类必须含有某个接口,可以自己实现,也可以不实现,而采用基类定义的缺省实现。
非虚函数:继承类必须含有某个接口,必须使用基类的实现。

 

(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(继承体系):

class Airport { ... }; // represents airports

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 航空公司应该引以为荣。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ljinddlj/archive/2007/12/07/1922189.aspx

你可能感兴趣的:(C/C++,c++,inheritance,function,class,hierarchy,interface)