C++继承详解之四——is-a接口继承和has-a实现继承

在学习继承的过程中,不管是在书中还是在网上找资料,都跟多态分不开,其中还有个很抓人眼球的问题,那就是书上总是说的is-a关系和has-a关系。
很多书中讲到继承时都会说:
public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类对象也都是一个父类对象。
protetced/private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,是 has-a 的关系原则。
那is-a,has-a原则究竟是什么呢。
1.is-a
举一个例子,有一个Horse类可以保存关于马的所有信息,身高体重等等,那么我们就可以从Horse类中派生出白马类,白马类包含所有Horse类的成员,在白马类中可以新增关于白马的成员,这个成员通常不用于Horse类。

class Horse
{
public:
    int Tall;
    int Weight; 
};
class WhiteHorse:public Horse
{
public:
    int Color;
};

上面的代码中,WhiteHorse多了颜色这个成员。
整体来看,is-a表示了一种是的关系。比如白马是马,香蕉是水果,老师是人这种关系。
2.has-a
has-a体现了有这个思想。
比如,午餐有香蕉。但是午餐不是香蕉。
其实私有跟保护继承体现了has-a原则是因为,私有跟保护继承是实现继承。
什么是实现继承呢?
实现继承的主要目标是代码重用,我们发现类B和类C存在同样的代码,因此我们设计了一个类 A,用于存放通用的代码,基于这种思路的继承称为实现继承。
我们可以说,午餐中存在香蕉。
还记得 C++继承详解之二——派生类成员函数详解(函数隐藏、构造函数与兼容覆盖规则)中提到过的赋值兼容规则的前提就是公有继承。
现在我们可以看看为什么前提必须是公有继承。
私有和保护继承都是基于has-a的,我们可以简单的理解为包含关系。赋值兼容规则中规定:

在需要基类对象的任何地方都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员,而且所有成员的访问控制属性也和基类完全相同。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。
那么就可得到,当为私有或保护继承的时候,是包含的关系,基类在派生类中是私有的。需要基类的时候是不能用派生类代替的。
下面我们看一下has-a的两种情况
一、包含

class Banana{..};

class Lauch
{
 private:
  class Banana;
 ......
};

二.、私有继承
c++还有另一种实现has-a关系的途径—-私有继承。
使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。这意味着基类方法将不会成为派生对象公有接口的一部分,但可以在派生类的成员函数中使用它们。
使用公有继承,基类的公有方法将成为派生类的公有方法。简而言之,派生类将继承基类的接口,这是is-a关系的一部分。使用私有继承,基类的公有方法将成为派生类的私有方法。简而言之,派生类不能继承基类的接口。正如从被包含对象中看到的,这种不完全继承是has-a关系的一部分。
因此私有继承提供的特性与包含相同:获得实现,但不获得接口。所以,私有继承也可以用来实现has-a关系。
三、使用包含还是私有继承
由于既可以使用包含,也可以使用私有继承来建立has-a关系。大多数c++程序员倾向于前者。不过私有继承所提供的特性确实比包含多。例如,假设类包含保护成员,则这样的成员在派生类中是可用的,但在继承层次机构外是不可用的。如果使用组合奖这样的类保护在另一类中,则后者将不是排成类,而是位于继承层次结构之外,因此不能访问保护成员。但通过继承的到的将是派生类,因此他能够访问保护成员。
另一种需要使用私有继承的情况是需要重新定义虚函数。派生类可以重新定义虚函数,但包含类不能。使用私有继承,重新定义的函数将只能在类中使用,而不是公有的。

你可能感兴趣的:(继承,对象,Class)