明明白白c++ 继承 接口继承和实现继承

来源:互联网 发布:vnr翻译软件 编辑:程序博客网 时间:2024/06/04 18:59

前言

继承,为什么要设计继承呢?这个是面向对象的一个重要概念,减少设计的复杂度。通过继承可以减少代码量。

c++中的继承形式比较多,公有继承,保护继承,和私有继承。如果是为了对付考试,那么你还是需要弄清楚这些概念。但是实际上公有继承比较有用,其他的可以忽略掉,大多数情况下他们很不实用,而且还用弄错。所以google推荐使用组合的方式来代替那些私有和保护继承。

组合和继承,has和is的关系。这里就不讨论了。


就继承的目的来看,包括接口继承和实现继承两种。

根据前面讲过的虚函数的概念,这这里又要用到。

本文参考

http://dev.yesky.com/218/2145218_2.shtml

一  纯虚函数。-----------接口继承

纯虚函数的作用是为了实现接口。

这里拿移动为例子吧,因为很多讲这里的时候都是拿形状为例子的。用自己的例子将一东西,才能让自己学习明白。

比如我做游戏设计的,人型和飞鸟两种角色。他们的移动肯定是不同的,会播放不用的动画效果。(例如dota里面的普通英雄和蝙蝠火焰状态)

#include <iostream>using namespace std;class character{public:   virtual void  move()=0;   void id();};class people:public character{public:virtual void  move();};class bird:public character{public:virtual void  move();};void people::move(){   cout<<"people move"<<endl;}void bird ::move(){  cout<<"bird move"<<endl;}int main(){ character c;//报错 people p; bird b;  p.move();  b.move(); c.move();//报错}

注释掉报错那句话上面的程序的输出结果是

people move

bird move

有几个细节需要注意:

1 如果你用class 定义类,那么类里面默认声明是private,类之间的继承关系是private,所以一定养成好的习惯明确用public,private,protected来表示,因为你很可能记混淆struct 和class的默认关键字。

2 如果是接口继承一定要定义成纯虚函数。可以认为只是个接口,随着子类不同,属性不用。父类没有明确的定义,是抽象的概念。

3 含义纯虚函数的类叫做抽象类,它不能声明对象,因为它是抽象的,它没有具体形态,所以也就不存在。


二 虚函数--------------接口继承+ 缺省实现

这里我们拿两个英雄来说,lina 和 jugg 火女和剑圣,大家都知道剑圣有跳劈,攻击时候会是攻击力的倍数值,但是普通英雄并没有。

#include <iostream>using namespace std;class character{public:   virtual void attact(character &c);   int getLife();    void setLife(int value);     void setAccactValue(int value);     int getAccactValue();   private:   int life;      int accactValue;};int character::getAccactValue(){   return accactValue;}void character::setAccactValue(int value){      accactValue = value;}int character::getLife(){return life;}void character::attact(character &c){    int life = c.getLife();   c.setLife(life - getAccactValue());   cout<<"common acctact"<<endl;}void character::setLife(int value){   if (value > 0)      life = value;   else      life = 0;}class lina:public character{};class jugg:public character{public:    virtual void attact(character &c);float randomRate();};float jugg::randomRate(){   //这里为了简化写成1.5,实际是个区间的随机数   return 1.5;}void jugg::attact(character &c){int life = c.getLife();c.setLife(life - getAccactValue() * randomRate());cout<<"jugg acctact"<<endl;}int main(){ lina l; jugg   j; l.setLife(100); l.setAccactValue(10); j.setLife(100); j.setAccactValue(10); l.attact(j); j.attact(l); cout<<"lina's life"<<l.getLife()<<endl; cout<<"jugg's life"<<j.getLife()<<endl;}

上面一段代码描述了jugg和lina相互攻击的过程。

其中有一个地方需要注意攻击函数传递的对象要加&,这样才能改变传入的对象,不然就是值传递。你会发现最后两个英雄打来打去,生命都不会减少。

http://blog.csdn.net/mlkiller/article/details/8754330如果不明白可以看这篇文章

输出如下:

common acctact
jugg acctact
lina's life85
jugg's life90

火女只会调用父类的普通攻击,而剑圣则会调用自己的攻击方式,所以导致相同生命,相同攻击力,最终结果不同。


所以,虚函数可以理解为很多情况下, 子类都可以用父类的缺省值,但是也有一些例外的,对例外情况进行重写。


三 非虚函数 (即能实现接口,又能强制实现)

其实一般会认为非虚函数就是,子类中不用重写的函数,例如上文中的getLife等。如果需要重写的,最好写成虚函数,非虚函数的隐藏的方式其实效果不好,对于有些情况也不适用。

我之前的那篇文章中也说了非虚函数和虚函数的一些区别,如果想了解可以查看

http://blog.csdn.net/mlkiller/article/details/8852200  明明白白c++ 虚函数

它和上面的虚函数最大的区别就是虚函数是动态绑定。

也就是说只有一种情况是不一样的,当用父类指针和引用的时候,它会根据指针或者引用的真实内容是属于父类还是子类,来绑定对于的函数。

这里就不展开讲了,另外java里面也没有非虚函数这种形式。


那它存在有两个原因,我想,

第一是历史原因,因为c++源于c语言,可以单独的定义函数。

   大家是从c转换到c++都很容易接受这种函数形式,直接加virtual反而不容易接受。

第二是效率上会稍微有点提高。但是如果所有的函数都用虚函数其实也没有坏处。

原创粉丝点击