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

有 n 种英雄,CYi 类中就会有 n 个 Attack 成员函数,以及 n 个 FightBack成员函数 。对于其他类也如此 。
如果游戏版本升级,增加了新的英雄寒冰艾希 CAshe ,则程序改动较大 。所有的类都需要增加两个成员函数:
void Attack(CAshe * pAshe);void FightBack(CAshe * pAshe);这样工作量是非常大的!!非常的不人性,所以这种设计方式是非常的不好!
03 多态的实现方式用多态的方式去实现,就能得知多态的优势了,那么上面的栗子改成多态的方式如下:
// 基类class CHero {public:virtual void Attack(CHero *pHero){}virtual voidFightBack(CHero *pHero){}virtual void Hurted(int nPower){}protected:int m_nPower ; //代表攻击力int m_nLifeValue ; //代表生命值};// 派生类 CYi:class CYi : public CHero {public:// 攻击函数void Attack(CHero * pHero){.... // 表现攻击动作的代码pHero->Hurted(m_nPower); // 多态pHero->FightBack(this);// 多态}// 减少自身生命值void Hurted(int nPower){... // 表现受伤动作的代码m_nLifeValue -= nPower;}// 反击函数void FightBack(CHero * pHero){....// 表现反击动作的代码pHero->Hurted(m_nPower/2); // 多态}};如果增加了新的英雄寒冰艾希 CAshe ,只需要编写新类 CAshe ,不再需要在已有的类里专门为新英雄增加:
void Attack( CAshe * pAshe) ;void FightBack(CAshe * pAshe) ;所以已有的类可以原封不动,那么使用多态的特性新增英雄的时候,可见改动量是非常少的 。
多态使用方式:
void CYi::Attack(CHero * pHero) {pHero->Hurted(m_nPower); // 多态pHero->FightBack(this);// 多态}CYi yi; CGaren garen; CLeesin leesin; CEzreal ezreal;yi.Attack( &garen );//(1)yi.Attack( &leesin ); //(2)yi.Attack( &ezreal ); //(3)根据多态的规则,上面的(1),(2),(3)进入到 CYi::Attack 函数后
,分别调用:
CGaren::HurtedCLeesin::HurtedCEzreal::Hurted多态的又一例子出一道题考考大家,看大家是否理解到了多态的特性,下面的代码,pBase->fun1() 输出结果是什么呢?
class Base {public:void fun1(){fun2();}virtual void fun2()// 虚函数{cout << "Base::fun2()" << endl;}};class Derived : public Base {public:virtual void fun2()// 虚函数{cout << "Derived:fun2()" << endl;}};int main() {Derived d;Base * pBase = & d;pBase->fun1();return 0;}是不是大家觉得 pBase 指针对象虽然指向的是派生类对象,但是派生类里没有 fun1 成员函数,则就调用基类的 fun1 成员函数,Base::fun1() 里又会调用基类的 fun2 成员函数,所以输出结果是 Base::fun2() ?
假设我把上面的代码转换一下,大家还觉得输出的是 Base::fun2() 吗?
class Base {public:void fun1(){this->fun2();// this是基类指针,fun2是虚函数,所以是多态}}this 指针的作用就是指向成员函数所作用的对象,所以非静态成员函数中可以直接使用 this 来代表指向该函数作用的对象的指针 。
pBase 指针对象指向的是派生类对象,派生类里没有 fun1 成员函数,所以就会调用基类的 fun1 成员函数,在 Base::fun1() 成员函数体里执行 this->fun2() 时,实际上指向的是派生类对象的 fun2 成员函数 。
所以正确的输出结果是:
Derived:fun2()所以我们需要注意:
在非构造函数,非析构函数的成员函数中调用「虚函数」,是多态!!!构造函数和析构函数中存在多态吗?在构造函数和析构函数中调用「虚函数」,不是多态 。编译时即可确定,调用的函数是 自己的类或基类 中定义的函数,不会等到运行时才决定调用自己的还是派生类的函数 。
我们看如下的代码例子,来说明:
// 基类class CFather {public:virtual void hello() // 虚函数{cout<<"hello from father"<<endl;}virtual void bye() // 虚函数{cout<<"bye from father"<<endl;}};// 派生类class CSon : public CFather{ public:CSon() // 构造函数{hello();}~CSon()// 析构函数{bye();}virtual void hello() // 虚函数{cout<<"hello from son"<<endl;}};int main(){CSon son;CFather *pfather;pfather = & son;pfather->hello(); //多态return 0;}【C++ 一篇搞懂多态的实现原理】输出结果:
hello from son// 构造son对象时执行的构造函数hello from son// 多态bye from father // son对象析构时,由于CSon类没有bye成员函数,所以调用了基类的bye成员函数多态的实现原理「多态」的关键在于通过 基类指针或引用 调用一个 虚函数 时,编译时不能确定到底调用的是基类还是派生类的函数,运行时才能确定 。


推荐阅读