C++ 一篇搞懂多态的实现原理

虚函数和多态
01 虚函数virtualvirtualclass Base {virtual int Fun() ; // 虚函数};int Base::Fun() // virtual 字段不用在函数体时定义{ }02 多态的表现形式一

  • 「派生类的指针」可以赋给「基类指针」;
  • 通过基类指针调用基类和派生类中的同名「虚函数」时:若该指针指向一个基类的对象,那么被调用是基类的虚函数;若该指针指向一个派生类的对象,那么被调用的是派生类的虚函数 。
这种机制就叫做“多态”,说白点就是 调用哪个虚函数,取决于指针对象指向哪种类型的对象  。
// 基类class CFather {public:virtual void Fun() { } // 虚函数};// 派生类class CSon : public CFather { public :virtual void Fun() { }};int main() {CSon son;CFather *p = &son;p->Fun(); //调用哪个虚函数取决于 p 指向哪种类型的对象return 0;}上例子中的 p 指针对象指向的是 CSon 类对象,所以 p->Fun() 调用的是 CSon 类里的 Fun 成员函数 。
03 多态的表现形式二
  • 派生类的对象可以赋给基类「引用」
  • 通过基类引用调用基类和派生类中的同名「虚函数」时:若该引用引用的是一个基类的对象,那么被调用是基类的虚函数;若该引用引用的是一个派生类的对象,那么被调用的是派生类的虚函数 。
这种机制也叫做“多态”,说白点就是 调用哪个虚函数,取决于引用的对象是哪种类型的对象  。
// 基类class CFather {public:virtual void Fun() { } // 虚函数};// 派生类class CSon : public CFather { public :virtual void Fun() { }};int main() {CSon son;CFather &r = son;r.Fun(); //调用哪个虚函数取决于 r 引用哪种类型的对象return 0;}}上例子中的 r 引用的对象是 CSon 类对象,所以 r.Fun() 调用的是 CSon 类里的 Fun 成员函数 。
04 多态的简单示例class A {public :virtual void Print() { cout << "A::Print"<<endl ; }};// 继承A类class B: public A {public :virtual void Print() { cout << "B::Print" <<endl; }};// 继承A类class D: public A {public:virtual void Print() { cout << "D::Print" << endl ; }};// 继承B类class E: public B {virtual void Print() { cout << "E::Print" << endl ; }};A类、B类、E类、D类的关系如下图:
C++ 一篇搞懂多态的实现原理

文章插图
 
int main() {A a; B b; E e; D d;A * pa = &a;B * pb = &b;D * pd = &d;E * pe = &e;pa->Print();// a.Print()被调用,输出:A::Printpa = pb;pa -> Print(); // b.Print()被调用,输出:B::Printpa = pd;pa -> Print(); // d.Print()被调用,输出:D::Printpa = pe;pa -> Print(); // e.Print()被调用,输出:E::Printreturn 0;}05 多态作用在面向对象的程序设计中使用「多态」,能够增强程序的 可扩充性 ,即程序需要修改或增加功能的时候,需要 改动和增加的代码较少  。
LOL 英雄联盟游戏例子下面我们用设计 LOL 英雄联盟游戏的英雄的例子,说明多态为什么可以在修改或增加功能的时候,可以较少的改动代码 。
LOL 英雄联盟是 5v5 竞技游戏,游戏中有很多英雄,每种英雄都有一个「类」与之对应,每个英雄就是一个「对象」 。
英雄之间能够互相攻击,攻击敌人和被攻击时都有相应的动作,动作是通过对象的成员函数实现的 。
下面挑了五个英雄:
  • 探险家 CEzreal
  • 盖楼 CGaren
  • 盲僧 CLeesin
  • 无极剑圣 CYi
  • 瑞兹 CRyze

C++ 一篇搞懂多态的实现原理

文章插图
 
基本思路:
  1. 为每个英雄类编写 Attack 、 FightBack 和 Hurted 成员函数 。
AttackFightBackHurted
  1. 设置基类 CHero ,每个英雄类都继承此基类

C++ 一篇搞懂多态的实现原理

文章插图
 
02 非多态的实现方法// 基类class CHero {protected:int m_nPower ; //代表攻击力int m_nLifeValue ; //代表生命值};// 无极剑圣类class CYi : public CHero {public:// 攻击盖伦的攻击函数void Attack(CGaren * pGaren){.... // 表现攻击动作的代码pGaren->Hurted(m_nPower);pGaren->FightBack(this);}// 攻击瑞兹的攻击函数void Attack(CRyze * pRyze){.... // 表现攻击动作的代码pRyze->Hurted(m_nPower);pRyze->FightBack( this);}// 减少自身生命值void Hurted(int nPower){... // 表现受伤动作的代码m_nLifeValue -= nPower;}// 反击盖伦的反击函数void FightBack(CGaren * pGaren){....// 表现反击动作的代码pGaren->Hurted(m_nPower/2);}// 反击瑞兹的反击函数void FightBack(CRyze * pRyze){....// 表现反击动作的代码pRyze->Hurted(m_nPower/2);}};


推荐阅读