is-a接口继承和has-a实现继承

来源:互联网 发布:淘宝配眼镜靠谱吗 编辑:程序博客网 时间:2024/05/18 22:40

 在学习继承的过程中,不管是在书中还是在网上找资料,都跟多态分不开,其中还有个很抓人眼球的问题,那就是书上总是说的is-a关系和has-a关系。 
  很多书中讲到继承时都会说: 
  public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类对象也都是一个父类对象。 
  protetced/private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,是 has-a 的关系原则。 
  那is-a,has-a原则究竟是什么呢。

1.is-a

  举一个例子,有一个Horse类可以保存关于马的所有信息,身高体重等等,那么我们就可以从Horse类中派生出白马类,白马类包含所有Horse类的成员,在白马类中可以新增关于白马的成员,这个成员通常不用于Horse类。

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">class Horse{<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span>:    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> Tall;    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> Weight; };class WhiteHorse:<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> Horse{<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span>:    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> Color;};</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>

  上面的代码中,WhiteHorse多了颜色这个成员。 
  整体来看,is-a表示了一种是的关系。比如白马是马,香蕉是水果,老师是人这种关系。

2.has-a

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

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

一、包含

<code class="hljs scala has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Banana</span>{</span>..};<span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Lauch</span></span>{ <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span>:  <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Banana</span>;</span> ......};</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li></ul>

二.、私有继承

  c++还有另一种实现has-a关系的途径—-私有继承。 
  使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。这意味着基类方法将不会成为派生对象公有接口的一部分,但可以在派生类的成员函数中使用它们。 
  使用公有继承,基类的公有方法将成为派生类的公有方法。简而言之,派生类将继承基类的接口,这是is-a关系的一部分。使用私有继承,基类的公有方法将成为派生类的私有方法。简而言之,派生类不能继承基类的接口。正如从被包含对象中看到的,这种不完全继承是has-a关系的一部分。 
  因此私有继承提供的特性与包含相同:获得实现,但不获得接口。所以,私有继承也可以用来实现has-a关系。

三、使用包含还是私有继承

  由于既可以使用包含,也可以使用私有继承来建立has-a关系。大多数c++程序员倾向于前者。不过私有继承所提供的特性确实比包含多。例如,假设类包含保护成员,则这样的成员在派生类中是可用的,但在继承层次机构外是不可用的。如果使用组合奖这样的类保护在另一类中,则后者将不是排成类,而是位于继承层次结构之外,因此不能访问保护成员。但通过继承的到的将是派生类,因此他能够访问保护成员。 
  另一种需要使用私有继承的情况是需要重新定义虚函数。派生类可以重新定义虚函数,但包含类不能。使用私有继承,重新定义的函数将只能在类中使用,而不是公有的。

0 0
原创粉丝点击