前言
继承,为什么要设计继承呢?这个是面向对象的一个重要概念,减少设计的复杂度。通过继承可以减少代码量。
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
火女只会调用父类的普通攻击,而剑圣则会调用自己的攻击方式,所以导致相同生命,相同攻击力,最终结果不同。
所以,虚函数可以理解为很多情况下, 子类都可以用父类的缺省值,但是也有一些例外的,对例外情况进行重写。
三 非虚函数 (即能实现接口,又能强制实现)
我之前的那篇文章中也说了非虚函数和虚函数的一些区别,如果想了解可以查看
http://blog.csdn.net/mlkiller/article/details/8852200 明明白白c++ 虚函数
它和上面的虚函数最大的区别就是虚函数是动态绑定。
也就是说只有一种情况是不一样的,当用父类指针和引用的时候,它会根据指针或者引用的真实内容是属于父类还是子类,来绑定对于的函数。
这里就不展开讲了,另外java里面也没有非虚函数这种形式。
那它存在有两个原因,我想,
第一是历史原因,因为c++源于c语言,可以单独的定义函数。
大家是从c转换到c++都很容易接受这种函数形式,直接加virtual反而不容易接受。
第二是效率上会稍微有点提高。但是如果所有的函数都用虚函数其实也没有坏处。