派生类public继承自基类,其中函数均是接口继承。
实现继承又分为缺省继承与强制继承,对应着虚函数与非虚函数。
我们在做项目时,对于任何事物都要抱有先描述再组织的心态。
因此,当描述事物为其构建框架尤其是存在继承(is-a)关系时,一定要搞清楚派生类与父类中函数关系。
基类函数类型 |
派生类继承方式 |
纯虚函数(pure virtual) |
接口继承 |
普通虚函数(impure virtual) |
接口继承+缺省实现继承
|
非虚函数(non-virtual) |
接口继承+强制实现继承
|
实现基类函数时,我们需要考虑基类该函数与派生类该函数关系后,再给予具体的实现方式。
什么意思?
对于无需实例化的基类,完全可以将其声明为抽象类,其中声明的纯虚(pure virtual)函数可以只给出声明。
但如果所有派生类中该函数实现相同时,那可以将基类该函数声明为普通虚函数,这样当派生类定义时,可以直接调用基类函数,也就是缺省实现继承。
但这样也有可能引发意想不到的意外:
以书中例子做演示:
我们先定义一个基类飞机,它会实现一个飞行方法,因为所有的飞机都能飞。
class Airplane
{
public:
virtual void fly()
{
...
}
};
之后派生出了各种不同型号的飞机...
class ModelA : public Airplane
{
...//没有重定义fly,会缺省继承自基类Airplane
};
class ModelB : public Airplane
{
...
};
两个派生类共用一个fly函数,因为它们性质相同(都能飞),即体现了相同特性还避免了代码复用,看起来很完美不是?
但此时如果需要新上线一款飞机,它与前两种的不同就在于飞行方式,又因为时间原因急于上架而忘记了对fly函数重定义,那灾难就发生了。
class ModelC : public Airplane
{
... //未重定义fly函数
};
当我们调用ModelC的飞行方法时,它会按A和B的方式进行,这显然大错特错。
ModelC c;
c.fly();//按A和B的方式飞行???
所以我们不妨将fly只提供一个接口给派生类,即只提供接口继承。这样当实现派生类时就会强迫它实现自己的fly版本。
class Airplane
{
public:
virtual void fly() = 0;
};
class ModelC : public Airplane
{
public:
void fly()//强迫实现,否则不能实例化
{
...
}
};
对于A和B而言,想体现相同特性又要避免重复,那么可以在基类中定义一个函数,供A和B显性调用即可。
class Airplane
{
public:
virtual void fly() = 0;
protected:
void defaultFly()
{
... //实现一个飞行方式
}
};
class ModelA : public Airplane
{
public:
void fly() { defaultFly(); }
...
};
class ModelB : public Airplane
{
public:
void fly() { defaultFly(); }
...
};
当然,这种方法也不是万全之策,因为它要求defaultFly函数应该是非虚(non-virtual)函数,任何一个派生类都不应该重定义它。
但假如一时糊涂不小心定义成了虚函数,那么对于像C那样的派生类就需要重定义defaultFly函数,但假如忘了呢?
对此我们可以有更好的方法:
将fly声明为纯虚函数并给出实现方法。派生类如果需要使用基类fly方法时可以通过作用域手动调用,也可以重写一份不一样的飞行方式。
class Airplane
{
public:
virtual void fly() = 0
{
... //实现一份共同的飞行方式
}
};
class ModelA : public Airplane
{
public:
void fly()
{ Airplane::fly(); }
};
class ModelB : public Airplane
{
public:
void fly()
{ Airplane::fly(); }
};
class ModelC : public Airplane
{
public:
void fly()
{
... //重写一份不同的飞行方式
}
};
go big or go home——facebook
如有错误,敬请斧正