多态

来源:互联网 发布:p值计算软件 编辑:程序博客网 时间:2024/06/14 07:37

我们在类的应用中会遇到这样的问题,当我们在子类中定义了与父类中原型相同的函数(上篇中提到的重定义)时,我们如果需要通过基类的指针来获取子类函数的调用就很难实现,为了解决这样的问题,C++就引入了虚函数的概念,C++中就是通过virtual 关键字对多态进行支持。

1、重写:编译器判断是否为虚函数,根据传的类型不同去调用不同的函数。

虚函数:通过关键字 virtual 修饰的函数。 

此时,子类中的重定义函数将变为重写函数。所以我们可以看出,多态实现的几个条件:1、继承   2、虚函数  3、基类指针指向派生类

有了多态之后,我们就可以引入这样两个概念:

1、)静态联编(早绑定):没有发生多态时的函数调用,在编译时就明白如何调用与实现

2、)动态联编(迟绑定):发生多态时,编译时并不知道如何调用与实现,在函数运行时才明白(同样的例子有 if 的条件判断之类)


2、通过基类指针释放派生类对象

通过基类的指针释放派生类,会调用 基类的析构函数,不会调用派生类的析构函数,会造成派生类的资源泄漏。

此时我们就需要将析构函数设为虚函数,这样就能通过基类来释放派生类对象了。

(这边需要注意一个问题,我们测试时需要调用delete,所以main 函数中定义的对象也需要从堆上new )


3、多态原理

如果类中有一个函数是虚函数,那么它将会在类中增加一个虚函数指针(vfptr)。该虚函数指针指向了一张虚函数表,其中存了当前类中所有虚函数

编译器并不会区分派生类类型。如果判断出是虚函数,则通过该指针去取这块地址上的虚函数指针,然后通过虚函数指针去虚函数表中去取同名函数

A a;// 基类B b;// 派生类A *pa = &b;// 定义一个基类指针去指向派生类对象pa->print();// 多态  调用 B 的print    pa->vfptr->print()

4、在构造中调虚函数

在构造函数中调用虚函数是不能实现多态的,虚函数指针是分步实现初始化的,一开始指向父类的虚表,初始化结束再指向自己的虚表。


5、基类指针指向派生类数组

一般来说,基类和派生类的指针步长不同(除非空子类,但这样的子类有什么意义)。

pa++  ==  pa + sizeof(A);pb++  ==  pb + sizeof(B);
所以,不要用基类指针去操作派生的数组。


6、纯虚函数与抽象类

为了让类更贴近现实,作为一个基类,它应该只是一个个对象的共同点的集合,它不应该能够去实例化对象,所以就有了纯虚函数:

纯虚函数:只要虚函数声明,没有函数定义,格式:函数声明后加上 = 0;

抽象类:含有纯虚函数的类叫做抽象类

抽象类不能实例化对象,派生类必须实现抽象类的纯虚函数,一旦派生类没有实现抽象类的纯虚函数,那么派生类也会是一个纯虚函数。


7、C++中使用抽象类实现接口

虽然C++中并没有接口的概念,而我们却可以利用C++中特殊的抽象类来实现这一概念,能够完成这样的模拟主要是因为多重继承下的接口模拟并不会产生二义性。

当然,作为接口的模拟,抽象类中应该全是纯虚函数的声明,不能有变量,不能有其他函数。

这边,作为一个资深LOL玩家,利用多态实现了这样一个简单的游戏框架搭建(当然,功能很基础,纯粹娱乐)。

虽然代码看起来很长,但真的大部分都是复制粘贴的内容,重复率很高。


gamemanager.h :

#ifndef __GAMEMANAGER_H_#define __GAMEMANAGER_H_#include <stdio.h>#include "Hero.h"class GameManager{public:void heroShow(){printf("1、金克丝\n");printf("2、艾希\n");printf("3、迦娜\n");printf("4、阿狸\n");}void seleteHero(){int cmd;printf("\n请选择你的英雄: ");scanf("%d",&cmd);switch (cmd){case 1:hero = new jinx;printf("%s\n\n",hero->getName());break;case 2:hero = new ashe;printf("%s\n\n",hero->getName());break;case 3:hero = new janna;printf("%s\n\n",hero->getName());break;case 4:hero = new ahri;printf("%s\n\n",hero->getName());break;}}void skinShow(){hero->skinShow();printf ("\n请选择一款皮肤: ");}void selectSkin(){hero->selectSkin();}void gameStart(){printf("正在载入游戏。。。\n");printf ("载入成功\n\n");hero->showHero();while(1){char ch[2];fgets(ch,2,stdin);switch(ch[0]){case 'q':hero->skill1();break;case 'w':hero->skill2();break;case 'e':hero->skill3();break;case 'r':hero->skill4();break;}}}// 启动游戏void run(){printf("欢迎来到召唤师峡谷!\n\n");// 展示英雄heroShow();// 选择英雄seleteHero();// 展示皮肤skinShow();// 选择皮肤selectSkin();// 开始游戏gameStart();}private:Hero *hero;};#endif//__GAMEMANAGER_H_

英雄类:hero.h 

#ifndef __HERO_H__#define __HERO_H__#include <string.h>#include <stdio.h>#include "Skin.h"class Hero{public:char *getName(){return name;}virtual void skinShow() = 0;virtual void selectSkin() = 0;virtual void showHero() = 0;virtual void skill1() = 0;virtual void skill2() = 0;virtual void skill3() = 0;virtual void skill4() = 0;protected:char name[20];Skin *skin;};class jinx : public Hero{public:jinx(){strcpy(name,"jinx");printf("\n规则,就是用来打破的——");}void skinShow(){printf("1、神龙烈焰\n");printf("2、黑帮狂花\n");printf("3、星之守护者\n");}void selectSkin(){int num;scanf("%d",&num);switch (num){case 1: skin = new jinx1;break;case 2:skin = new jinx2;break;case 3:skin = new jinx3;break;}printf ("\n当前选择皮肤:%s\n\n",skin->getName());}void showHero(){printf("显示出金克丝\n");skin->showSkin();}virtual void skill1(){printf("枪炮交响曲\n");}virtual void skill2(){printf("震荡电磁波\n");}virtual void skill3(){printf("嚼火者手雷\n");}virtual void skill4(){printf("超究极死神飞弹\n");}};class ashe : public Hero{public:ashe(){strcpy(name,"ashe");printf("\n我将为您指路——");}void skinShow(){printf("1、源计划\n");printf("2、冠军之箭\n");printf("3、女皇\n");}void selectSkin(){int num;scanf("%d",&num);switch (num){case 1: skin = new ashe1;break;case 2:skin = new ashe2;break;case 3:skin = new ashe3;break;}printf ("\n当前选择皮肤:%s\n\n",skin->getName());}void showHero(){printf("显示出艾希\n");skin->showSkin();}virtual void skill1(){printf("射手的专注\n");}virtual void skill2(){printf("万箭齐发\n");}virtual void skill3(){printf("鹰击长空\n");}virtual void skill4(){printf("魔法水晶箭\n");}};class janna : public Hero{public:janna(){strcpy(name,"janna");printf("\n风之化身,听候您的差遣——");}void skinShow(){printf("1、女主播\n");printf("2、星之守护者\n");printf("3、寒冰女神\n");}void selectSkin(){int num;scanf("%d",&num);switch (num){case 1: skin = new janna1;break;case 2:skin = new janna2;break;case 3:skin = new janna3;break;}printf ("\n当前选择皮肤:%s\n\n",skin->getName());}void showHero(){printf("显示出迦娜\n");skin->showSkin();}virtual void skill1(){printf("飓风呼啸\n");}virtual void skill2(){printf("和风守护\n");}virtual void skill3(){printf("风暴之眼\n");}virtual void skill4(){printf("复苏季风\n");}};class ahri : public Hero{public:ahri(){strcpy(name,"ahri");printf("\n我们心有灵犀,不是么——");}void skinShow(){printf("1、偶像歌手\n");printf("2、星之守护者\n");printf("3、勇者\n");}void selectSkin(){int num;scanf("%d",&num);switch (num){case 1: skin = new ahri1;break;case 2:skin = new ahri2;break;case 3:skin = new ahri3;break;}printf ("\n当前选择皮肤:%s\n\n",skin->getName());}void showHero(){printf("显示出阿狸\n");skin->showSkin();}virtual void skill1(){printf("欺诈宝珠\n");}virtual void skill2(){printf("妖异狐火\n");}virtual void skill3(){printf("魅惑妖术\n");}virtual void skill4(){printf("灵魄突袭\n");}};#endif // __HERO_H__ 

皮肤类:skin.h

#ifndef __SKIN_H__#define __SKIN_H__#include <string.h>#include <stdio.h>class Skin{public:char *getName(){return name;}virtual void showSkin() = 0;protected:char name[20];};class jinx1:public Skin{public:jinx1(){strcpy(name,"神龙烈焰");}void showSkin(){printf ("显示神龙烈焰\n\n");}};class jinx2:public Skin{public:jinx2(){strcpy(name,"黑帮狂花");}void showSkin(){printf ("显示黑帮狂花\n\n");}};class jinx3:public Skin{public:jinx3(){strcpy(name,"星之守护者");}void showSkin(){printf ("显示星之守护者\n\n");}};class ashe1:public Skin{public:ashe1(){strcpy(name,"源计划");}void showSkin(){printf ("显示源计划\n\n");}};class ashe2:public Skin{public:ashe2(){strcpy(name,"冠军之箭");}void showSkin(){printf ("显示冠军之箭\n\n");}};class ashe3:public Skin{public:ashe3(){strcpy(name,"女皇");}void showSkin(){printf ("显示女皇\n\n");}};class janna1:public Skin{public:janna1(){strcpy(name,"女主播");}void showSkin(){printf ("显示女主播\n\n");}};class janna2:public Skin{public:janna2(){strcpy(name,"星之守护者");}void showSkin(){printf ("显示星之守护者\n\n");}};class janna3:public Skin{public:janna3(){strcpy(name,"寒冰女皇");}void showSkin(){printf ("显示寒冰女皇\n\n");}};class ahri1:public Skin{public:ahri1(){strcpy(name,"偶像歌手");}void showSkin(){printf ("显示偶像歌手\n\n");}};class ahri2:public Skin{public:ahri2(){strcpy(name,"星之守护者");}void showSkin(){printf ("显示星之守护者\n\n");}};class ahri3:public Skin{public:ahri3(){strcpy(name,"勇者");}void showSkin(){printf ("显示勇者\n\n");}};#endif //__SKIN_H__

主函数:

#include <stdio.h>#include "Gamemanager.h"int main(){GameManager GM;GM.run();return 0;}


原创粉丝点击