C++面向对象继承与多态( 二 )


游戏的描述如下:

  • 游戏有一个英雄角色 , 角色属性有生命(hp)和攻击力(ack)
  • 英雄可以对怪物进行攻击 , 同时也会受到怪物的攻击
  • 怪物属性有生命(hp)和攻击力(ack)
  • 怪物可以对英雄进行攻击 , 也会受到英雄的攻击
  • 现阶段有三种怪物:狼人 , 僵尸 , 女巫
我们先来实现怪物类:
class wolf //狼人类 {public:wolf(int hp, int ack): hp(hp), ack(ack){}bool damage(int dm) {if (this->hp <= 0) return false;this->hp -= dm;return false;}bool attack(hero &hr) {return hr.damage(this->ack);}private:int hp;int ack;};class zombie {public:zombie(int hp, int ack): hp(hp), ack(ack){}bool damage(int dm) {if (this->hp <= 0) return false;this->hp -= dm;return false;}bool attack(hero &hr) {return hr.damage(this->ack);}private:int hp;int ack;};class witch {public:witch(int hp, int ack): hp(hp), ack(ack){}bool damage(int dm) {if (this->hp <= 0) return false;this->hp -= dm;return false;}bool attack(hero &hr) {return hr.damage(this->ack);}private:int hp;int ack;};然后我们来实现英雄类:
class hero {public:hero(int hp, int ack): hp(hp), ack(ack){}bool damage(int dm) {if (this->hp <= 0) return false;this->hp -= dm;}bool attack(wolf &wf) {return wf.damage(this->ack);}bool attack(zombie &zb) {return zb.damage(this->ack);}bool attack(witch &wt) {return wt.damage(this->ack);}private:int hp;int ack;};我们发现 , 同样逻辑的attack()函数 , 我们需要实现三次 。如果后期游戏要增添新的怪物 , 我们还得继续写attack()函数 。这其实还是一种面向过程的思想 , 并不是说写几个类出来就是面向对象了 。而且这也完全不符合我们程序猿的编程习惯 , 我们程序猿不喜欢重复的东西 。欸 , 这个时候多态就能发挥他的作用了 。
我们来定义怪物们的基类:
class monster {public:virtual bool damage(int dm) = 0;virtual bool attack(hero &hr) = 0;};之前说了 , 我们并不关心这个基类的虚函数具体是怎么实现的 , 那么我们就可以将其声明为纯虚类 。然后让怪物都继承这个基类 , 实现上面这两个函数就可以了 。这样我们就可以将hero类改造成这样:
class hero {public:hero(int hp, int ack): hp(hp), ack(ack){}bool damage(int dm) {if (this->hp <= 0) return false;this->hp -= dm;}bool attack(monster &ms) //参数修改为monster类一定要用指针或者引用 {return ms.damage(this->ack);}private:int hp;int ack;};这样代码是不是就简洁很多 。而且根据多态的性质 , 不同的怪物会调用其各自的damage()函数 。以后要是新增怪物 , 只要继承和实现虚基类就好了 , hero类并不需要进行修改 。这就体现了面向对象编程的优势了 , 这还只是其中之一 。
同理 , 要是有多种英雄 , 我们同样可以抽象出一个英雄类的虚基类 , 然后派生出各式各样的英雄 , 怪物类也不需要重复写多个attack()函数 。
有同学还是觉得怪物类的实现还是重复度太高了 , 这没有体现多态的优势啊 。其实不然 , 前面说到每个子类都应该重写基类的虚函数 , 是因为不同的子类都应该有他的特别之处 ,  所以才叫派生嘛 。如果子类和子类 , 或者子类和基类完全一样那就没有必要继承与派生了 。
这里重复度高只是因为代码量小 , 我只是举了个小小的例子 , 其实在真正的游戏中不同怪物子类的attack()函数和damage()函数的内部细节应该是不一样 。比如不同的怪物有不同的攻击特效 , 有不同的受击效果 , 有不同的技能冷却时间等等 。这些细节都是通过子类去重写基类的虚函数 , 才得以体现的 。
总结到此为止 , 我所了解的继承与多态算是总结完毕了 。会简单地封装几个类并不是面向对象编程 , 只有彻底理解了封装、继承与多态 , 面向对象编程才算是入了个门 。只有理解了这些 , 我们才能开始学习设计模式 , 才能领悟到设计模式的精髓所在 。学设计模式建议大家去看《大话设计模式》这本书 , 以后有时间我也会在我的博客里总结一些设计模式 。

【C++面向对象继承与多态】


推荐阅读