在public继承中,(public 继承表示的关系是 “is a ” 的关系),其类中定义的函数主要有三类:pure virtual函数,impure virtual函数以及non-virtual函数。
这三种函数决定了public继承中的两个重要概念 : “ 函数接口继承” 以及 “函数实现继承”。那么接下来将针对这样一个不简单的问题做一些简介。
考虑如下的类定义:
class Shape{
public:
virtual void show() const = 0;
virtual void error(const string &) ;
int getID() const;
....
};
class Rectangular : public Shape { ... };
class Ellipse : public Shape { ... } ;
对于上述类,首先做一些简单的说明:
Shape是抽象类,因为其中含有纯虚函数( show函数 ), 抽象类不能定义对象,值得注意的是,虽然show函数是纯虚函数,但是并不表示Shape不可以为其定义函数体。Shape可以为show函数定义函数体,但是对该函数的调用显然不能由对象来调用(因为Shape不能定义对象),因此只能 Shape::show() ;来调用。
接下来,就是重要的部分了。
身为class的设计者,有时候希望derived class只继承基类的成员函数的接口,有时候又需要同时继承函数的接口以及实现,有时候又需要重写(override)它们所继承的实现,有时候又希望derived同时继承接口与实现,并且不允许重写。
这些复杂的关系,接下来根据上述类的定义一步一步的解析:
Shape的成员函数的接口总是被继承。 由于 public 继承的 “is a ” 关系,也就是说任何可以施加到base class上的行为都可以施加到derived class身上。至于derived class如何使用他们,这便与 上述3种 virtual 函数有关。
首先,pure virtual 纯虚函数。 它必须被 任何继承了它的class 重新声明且定义,因为他们在抽象class类通常没有定义(只是通常,原因见上)。
1 声明pure virtual 函数 是为了让derived class继承它的接口(也没有函数体可以继承 啊~~)。只是告诉 derived class自行去去定义自己的实现。
2 声明impure virtual函数 是为了让derived class继承它的接口以及缺省实现(当然derived class也可以自己重新实现,多态~~)
但是,这样会遇到问题,如果新添加的derived class忘记定义自己的impure virtual函数体,也就意味着它直接使用base class的定义,这可能违背本身derived class的操作,造成意想不到的结果。对于这种问题,我们可以使用 pure virtual + default action 来完成。例如下面的代码:
class Airplane
{
public:
virtual void fly(string &destination); //虚函数,发出飞机飞行的目的地的指令
...
};
class SecA : public Airplane { ... } ; //SecA 与 SecB直接使用的base class的默认fly定义
class SecB : public Airplane { ... } ;
//先新添加一条航线SecC,但是忘记定义了的fly函数指定目的地,因此C航线,依旧飞的是原来的路线,这恐怕不是你想要的吧。
class SecC : public Airplane {...};
因此,使用 pure virtual + default action来改进,有:
class Airplane
{
public:
virtual void fly(string &destination) = 0; //纯虚函数,由各个航线自己指定目的地
...
protected:
void defaultFly(const string &destination);
};
void Airplane :: defaultFly (const string &destination))
{
//default fly strategy
}
//SecA 与 SecB自行实现fly函数决定目的地。
class SecA : public Airplane
{
virtual void fly(string &destination)
{
Airplane::defaultFly(destination);
}
...
} ;
class SecB : public Airplane
{
virtual void fly(string &destination)
{
Airplane::defaultFly(destination);
}
...
} ;
值得注意的是:上面的defaultFly函数是non-virtual的,因为没有任何函数需要重新定义它~~
试想,如果 defaultFly 函数是virtual的,那么某些类忘记定义了怎么办,额~~好熟悉的话,又得循环回去了~~
注意,在上面提到过pure virtual函数也是可以有定义体的,那么我们就可以不用声明 defaultFly 函数了,实现接下来的部分:
class Airplane
{
public:
virtual void fly(string &destination) = 0; //纯虚函数,由各个航线自己指定目的地
...
};
void Airplane :: fly (string &destination))
{
//default fly strategy
}
//SecA 与 SecB自行实现fly函数决定目的地。
class SecA : public Airplane
{
virtual void fly(string &destination)
{
Airplane::fly(destination); //通过类名调用纯虚函数的实现
}
...
} ;
class SecB : public Airplane
{
virtual void fly(string &destination)
{
Airplane::fly(destination);
}
...
} ;
最后,对于non-virtual的函数,很简单,derived class 不能重新定义,只能无条件的继承该函数的接口与实现。
综上所述:定义virtual (pure / impure)以及non-virtual 是取决于类的设计者的意图:
在public 继承下,derived class 总是继承base class的全部接口。
pure virtual函数 指定了继承的接口。
impure virtual函数 指定了继承接口以及一份缺省实现。
non-virtual函数 指定了继承接口以及一份强制的实现。