c++复习3.虚函数(实验五第1题、作业4)

实验五第1题

实验五 虚函数表及其多态性的实现

一.实验目的

1.理解并掌握虚函数表的内部实现机制及其遍历方法;

2.理解并掌握利用虚函数表实现多态性的原理和使用方法;

3.理解并掌握虚析构函数的原理和使用方法;

二. 实验内容

1.理解并掌握虚函数表的内部实现机制及其遍历方法

第1题:下面是一个通过类的实例得到该类虚函数表的程序,请根据程序中注释的要求,完善该程序。

#include <iostream>

using namespace std;

class Base {

            public:

            virtual void f() { cout << "Base::f" << endl; }

            virtual void g() { cout << "Base::g" << endl; }

            virtual void h() { cout << "Base::h" << endl; }};

          typedef void(*Fun)(void);     //定义一个函数指针类型Fun

          int main(){

          Base b;

           Fun pFun = NULL;

            cout << "虚函数表地址:" <<* (long*)(&b) << endl;

            cout << "虚函数表中— 第一个虚函数地址:" << *(long*)*(long*)(&b) << endl;

            // Invoke the first virtual function

            pFun = (Fun)*((long*)*(long*)(&b));

//cout <<(long*)pFun<<endl;

           pFun(); 

//请仿造上面的方法,改动程序相应的语句,能够分别输出Base::g()Base::h()的函数地址。

           // (Fun)*((int*)*(int*)(&b)+0);  // Base::f()    32位计算机

         // (Fun)*((int*)*(int*)(&b)+1);  // Base::g()      32位计算机

          //  (Fun)*((int*)*(int*)(&b)+2);  // Base::h()    32位计算机

 }

  1. 请给出程序完整源代码,并粘贴程序运行结果截图。
#include <iostream>
using namespace std;
class Base {
public:
	virtual void f() { cout << "Base::f" << endl; }
	virtual void g() { cout << "Base::g" << endl; }
	virtual void h() { cout << "Base::h" << endl; }
};
typedef void(*Fun)(void);     //定义一个函数指针类型Fun
int main() {
	Base b;
	Fun pFun = NULL;
	cout << "虚指针的值,即:虚函数表地址:" << (long*)(*(long*)(&b)) << endl << endl;
	cout << "虚表vptr[0]的内容,即:虚函数表 — 第1个虚函数f()地址:" << (long*)(*((long*)(*(long*)(&b)) + 0)) << endl;
	cout << "虚表vptr[1]的内容,即:虚函数表 — 第2个虚函数f()地址:" << (long*)(*((long*)(*(long*)(&b)) + 2)) << endl;  //绿色部分为虚指针
	cout << "虚表vptr[2]的内容,即:虚函数表 — 第3个虚函数f()地址:" << (long*)(*((long*)(*(long*)(&b)) + 4)) << endl << endl;
	//也可以用给函数指针赋值的方法
	pFun = (Fun)(*(long*)(*(long*)(&b)));
	cout << "虚函数表 — 第1个函数f()地址:" << (long*)pFun << endl;
	pFun();
	pFun = (Fun)(*((long*)(*(long*)(&b)) + 2));
	cout << "虚函数表 — 第2个函数g()地址:" << (long*)pFun << endl;
	pFun();
	pFun = (Fun)(*((long*)(*(long*)(&b)) + 4));
	cout << "虚函数表 — 第3个函数h()地址:" << (long*)pFun << endl;
	pFun();
	system("pause");
}

(2) 请根据程序运行结果,画出Base类虚函数表的示意图。

作业4

一、从虚函数的内部实现机制,理解利用虚函数实现的C++的多态性(动态绑定)

  1. 请阅读下列程序,然后回答问题:

#include <iostream>

using namespace std;

class Base1 { //基类Base1定义

public:

      virtual void display() const;    //虚函数

};

void Base1::display() const {

      cout << "Base1::display()" << endl;

}

class Base2:public Base1 { //公有派生类Base2定义

public:

      void display() const;  //覆盖基类的虚函数

};

void Base2::display() const {

      cout << "Base2::display()" << endl;

}

class Derived: public Base2 { //公有派生类

public:

      void display() const; //覆盖基类的虚函数

};

void Derived::display() const {

      cout << "Derived::display()" << endl;

}

void fun(Base1 *ptr) { //参数为指向基类对象的指针

      ptr->display();    //"对象指针->成员名"

}

int main() {  //主函数

      Base1 base1;     //定义Base1类对象

      Base2 base2;     //定义Base2类对象

      Derived derived; //定义Derived类对象

      //cout<<sizeof(base1)<<endl;

      //cout<<sizeof(base2)<<endl;

      //cout<<sizeof(derived)<<endl;

      fun(&base1);//用Base1对象的指针调用fun函数

      fun(&base2);//用Base2对象的指针调用fun函数

      fun(&derived);//用Derived对象的指针调用fun函数

      return 0;

}

  1. 写出上面程序的运行结果
  2. 画出上面程序创建的base1,base2,derived 三个对象的内存结构及各自的虚函数指针,虚函数表和装入内存的Base1::display(),Base2::display(),Derived::display()的相互关系。
  3. 上面程序中哪个语句体现了c++的多态性?是如何体现的?
  4. 根据对上述程序的理解,请给出实现c++的多态性,需要具备哪些条件?

答:(1)

(2)

(3)void fun(Base1 *ptr)语句体现了程序的多态性。根据C++的类型转换规则,基类指针可以指向派生类对象,当基类指针ptr 指向派生类对象base2时,程序执行派生类对象base2的类Base2对 display()的重写函数:Base2:: display(),当基类指针ptr 指向派生类对象derived时,程序执行派生类对象derived的类Derived对 display()的重写函数:Derived:: display(),从而利用虚函数实现了C++的多态性。

(4) 需要具备三个条件:有继承;有虚函数重写;用父类指针(父类引用)指向子类对象。

二.从开发实际应用的功能含义方面,理解C++虚函数实现多态的灵活性和可扩展性

1. 下面的程序是一款打斗小游戏中类设计的框架:

  • 游戏描述如下:

(1)游戏有骑士、天使、狼、鬼等多种角色;

(2) 每种角色都有生命力、攻击力两种属性;

(3)每种角色都有攻击,受伤,反击三种行为;

(4)关于攻击行为:当一个角色攻击另一个角色时,被攻击者会受伤;同时,被攻击者会反击;

(5)关于受伤行为:攻击者生命值减半(具体释义见代码段中的注释);

(6)关于反击行为:每一种角色反击的力量较弱,只是其自身攻击力的 1/2(具体释义见代码段中的注释);

  • 类设计框架如下:(请忽略代码中因word格式,造成的偶尔首字母大小写不符合语法的问题)

class CDragon

{

private:

      int power;  //攻击力

      int lifeValue;  //生命值

public:

      void Attack(CWolf * p);  //攻击“狼”的成员函数

      void Attack(CGhost* p);  //攻击“鬼”的成员函数

      //……其他 Attack 重载函数

      //表现受伤的成员函数

      void Hurted(int nPower);

      void FightBack(CWolf * p);  //反击“狼”的成员函数

      void FightBack(CGhost* p);  //反击“鬼”的成员函数

      //......其他FightBack重载函数

};

各成员函数如下:

void CDragon::Attack(CWolf* p)    // 参数 p 指向被攻击的 CWolf 对象

{

      p->Hurted(power);        // 在 p 所指向的对象上执行 Hurted 成员函数,使被攻击的“狼”对象受伤;调用 Hurted 成员函数时,参数是攻击者“龙”对象的攻击力

      p->FightBack(this);      //以指向攻击者自身的 this 指针为参数,调用被攻击者的 FightBack 成员函数,接受被攻击者的反击

}

void CDragon::Attack(CGhost* p)

{

      p->Hurted(power);

      p->FightBack(this);

}

void CDragon::Hurted(int nPower)

{

      lifeValue -= nPower;

}

void CDragon::FightBack(CWolf* p) // 一个对象的 Hurted 成员函数被调用,会导致该对象的生命值减少,减少的量等于攻击者的攻击力;在真实的程序中,Hurted 成员函数还应包含表现受伤时动作的代码,以及生命值如果减至小于或等于零,则倒地死去的代码。

{

      p->Hurted(power / 2);

}

void CDragon::FightBack(CGhost* p)// p 指向的是实施攻击者;对攻击者进行反击,实际上就是调用攻击者的 Hurted 成员函数使其受伤;其受到的伤害的大小等于实施反击者的攻击力的一半(反击的力量不如主动攻击大);FightBack 成员函数中其实也应包含表现反击动作的代码。

{

      p->Hurted(power / 2);

}

  • 上述游戏将每一种角色设计为一个类,一个角色就是该类的一个对象,没有应用继承和多态特性,针对上述设计,请回答以下问题:

1)上述游戏中如果有 n 种角色,CDragon 类中要有多少个Attack 成员函数用于攻击 n 种角色?要有多少个 FightBack 成员函数(以上两个问题,假设两条龙也能互相攻击)?对于其他类,是不是也是如此呢?

答:CDragon 类中要有n个Attack 成员函数,要有n个FightBack 成员函数,对于其他类,也是如此。

(2)现应游戏版本升级的需要,将增加新的角色“雷鸟”,假设其类名为 CThunderBird,那么,在现有的类框架下(没有应用继承和多态),程序需要做哪些改动呢?如果在角色类很多的情况下,这种设计会有什么弊端?

答:除了需要编写一个 CThiinderBird 类外,所有的类都需要增加Attack 成员函数和FightBack 成员函数,用以对“雷鸟”实施攻击,以及在被“雷鸟”攻击时对其进行反击。

void Attack(CThunderBird* p);

void FightBack(CThunderBird* p);

弊端是:在角色种类很多的情况下,工作量会增加很多。

(3) 如果应用继承改进上述的设计:将 CDragon、CWolf 等类的共同特点抽取出来,形成一个 CCreature 类,然后再从 CCreature 类派生出 CDragon、CWolf 等类,这样的改进是否能够明显精简上面的代码设计?请说明原因。

答:不能。因为,每种角色进行攻击、反击和受伤时的表现动作各不相同,每一种角色,例如: CDmgon、CWdf 这些类还要实现各自的 Hurted 成员函数,以及一系列 Attack、FightBack 成员函数。所以,即使抽象出各种角色的父类,但是各个类共同的行为(抽象为:父类的Public成员函数)有限,每个类仍然需要编写大量自己的成员函数,以实现各自类自己的行为;因此,对本游戏的设计,引人基类 CCreature,并不能从根本上精简程序代码。

4)引入多态机制能够完美解决上述游戏的功能扩充问题,大大精简之前的设计代码,下面是应用多态后的改进代码,请在此基础上,编写新角色“雷鸟”类CThunderBird的设计。

class CCreature { 

//设置一个基类 CCreature,概括所有角色的共同特点;所有具体的角色类,如 CDragon、CWolf、CGhost 等,均从 CCreature 类派生而来

protected:

    int lifeValue, power;

public:

    virtual void Attack(CCreature* p) {};

    virtual void Hurted(int nPower) {};

    virtual void FightBack(CCreature* p) {};

};

class CDragon : public CCreature

{

public:

    virtual void Attack(CCreature* p) {

        p->Hurted(power);

        p->FightBack(this);

    }

    virtual int Hurted(int nPower) {

        lifeValue -= nPower;

    }

    virtual int FightBack(CCreature* p) {

        p->Hurted(power / 2);

    }

};

答:CThunderBird 类的设计,不需要在已有的各个类中专门为新角色增加 void Attack(CThunderBird * p) 和 void FightBack(CThunderBird* p)这两个成员函数,即:其他类不用做任何修改,只需要编写新类 CThunderBird 即可。新类 CthunderBird的设计同Cdragon类的设计,只需将Cdragon类设计中的dragon替换为ThunderBird即可。代码(略)

(5)根据下面主函数中的代码,请指出dragon实现了对哪几种动物的攻击?并结合语句,说明dragon是如何实现对这些动物的攻击的?

int main(){

CDragon dragon;

CWolf wolf;

CGhost ghost;

CThunderBird bird;

Dragon.Attack(&wolf);

Dragon.Attack(&ghost);

Dragon.Attack(&bird);}

答:dragon实现了对三个角色:wolf,ghost和bird的攻击。根据赋值兼容规则,上面第 5、6、7 行中的参数都与基类指针类型 CCreature* 相匹配,系统自动将基类引用转为对派生类wolf,ghost和bird的引用,从 5、6、7 三行进入 CDragon::Attack 函数后,执行 p-> Hurted(power) 语句时,p 分别指向的是 wolf、ghost 和 bird,根据对多态虚函数指针的调用,分别调用的就是 CWolf::Hurted、CGhost::Hurted 和 CBird: Hurted 函数,从而实现了对wolf,ghost和bird的攻击。

(6)如果要实现对其他动物,例如:fox,tiger的攻击,上述程序需要做怎样的改动?

答:需要增加Fox 类和Tiger类,并在主函数中增加如下的语句:

CFox fox;

CTiger tiger;

fox.Attack(&wolf);

tiger.Attack(&ghost);

(7)经过上述的思考和分析,请说明改进后的游戏代码,是如何实现多态的?并总结多态的一般性实现方法。

答:首先,以基类的虚函数virtual void Ccreature:: Attack(CCreature* p) {};为入口函数,各个派生类Dragon、 Wolf、Ghost 和Bird通过重写基类的虚函数,从而在各自类的对象中获得指向自己类虚函数的虚指针,那么,当父类的指针指向该派生类时,调用的就是该派生类的虚函数;如果有多个派生类,则实现了多态。

多态的一般性实现方法:基类设有虚函数,各个派生类重写该虚函数,当主函数中,父类的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。

  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值