关闭

C++多态和虚函数

标签: C++多态虚函数
1304人阅读 评论(0) 收藏 举报
分类:

参考:微学院 c++入门教程

介绍一个RPG游戏的时候,我们就说到不同职业的玩家在发动普通攻击和特殊攻击时,有着不同的效果。在编写程序的时候,我们并不知道用户会选择哪种职业的玩家,那么又该如何保证各种攻击效果和用户选择的玩家是对应的呢?


在使用继承的时候,子类必然是在父类的基础上有所改变。如果两者完全相同,这样的继承就失去了意义。同时,不同子类之间具体实现也是有所区别的,否则就出现了一个多余的类。不同的类的同名成员函数有着不同的表现形式,称为多态性多态性是符合人的认知规律的,即称呼相同,所指不同。比如,学生类及其子类都有学习这个成员函数,但本科生、中学生、小学生的学习内容并不相同;玩家类的子类都有攻击这项技能,但剑士、弓箭手和魔法师的攻击方法不同。

多态性往往只有在使用对象指针或对象引用时才体现出来。编译器在编译程序的时候完全不知道对象指针可能会指向哪种对象(引用也是类似的情况),只有到程序运行了之后才能明确指针访问的成员函数是属于哪个类的。我们把C++的这种功能称为“滞后联编”。多态性是面向对象的一个标志性特点,没有这个特点,就无法称为面向对象。

多态能够方便我们编写程序,可以让不同的类与它独特的成员函数一一对应。即使我们只是简单地“称呼”,程序也会很明白我们的心思。那么,多态应该如何实现呢?

多态的实现

在C++中,我们把表现多态的一系列成员函数设置为虚函数。虚函数可能在编译阶段并没有被发现需要调用,但它还是整装待发,随时准备接受指针或引用的“召唤”。设置虚函数的方法为:在成员函数的声明最前面加上保留字virtual。注意,不能把virtual加到成员函数的定义之前,否则会导致编译错误。

下面我们把各种学生的学习都设置为虚函数,了解如何实现多态:(程序17.7.1)
//student.h
#include <iostream>
using namespace std;
class student
{
   public:
   student(char *n,int a,int h,int w);
   student();
   void set(char *n,int a,int h,int w);
   char * sname();
   int sage();
   int sheight();
   int sweight();
   virtual void study();//把学习设置为虚函数
   protected:
   char name[10];
   int age;
   int height;
   int weight;
};
char * student::sname()
{
   return name;
}
int student::sage()
{
   return age;
}
int student::sheight()
{
   return height;
}
int student::sweight()
{
   return weight;
}
void student::set(char *n,int a,int h,int w)
{
   int i;
   for (i=0;n[i]!='\0';i++)
   {
      name[i]=n[i];
   }
   name[i]='\0';
   age=a;
   height=h;
   weight=w;
   return;
}
student::student(char *n,int a,int h,int w)
{
   cout <<"Constructing a student with parameter..." <<endl;
   set(n,a,h,w);
}
student::student()
{
   cout <<"Constructing a student without parameter..." <<endl;
}
void student::study()//成员函数定义处没有virtual
{
   cout <<"随便学些什么。" <<endl;
   return;
}
//undergraduate.h
#include "student.h"
class Undergraduate:public student
{
   public:
   double score();
   void setGPA(double g);
   bool isAdult();
   virtual void study();//把学习设置为虚函数
   protected:
   double GPA;
};
double Undergraduate::score()
{
   return GPA;
}
void Undergraduate::setGPA(double g)
{
   GPA=g;
   return;
}
bool Undergraduate::isAdult()
{
   return age>=18?true:false;
}
void Undergraduate::study()//成员函数定义处没有virtual
{
   cout <<"学习高等数学和大学英语。" <<endl;
   return;
}
//pupil.h
class Pupil:public student
{
   public:
   virtual void study();//把学习设置为虚函数
};
void Pupil::study()
{
   cout <<"学习语数外。" <<endl;
   return;
}
//main.cpp
#include <iostream>
#include "undergraduate.h"
#include "pupil.h"
using namespace std;
int main()
{
   Undergraduate s1;
   student s2;
   Pupil s3;
   student *sp=&s1;//sp指向本科生对象
   s1.set("Tom",21,178,60);
   sp->study();//体现多态性
   sp=&s2; //sp指向学生对象
   s2.set("Jon",22,185,68);
   sp->study();//体现多态性
   sp=&s3; //sp指向小学生对象
   s3.set("Mike",8,148,45);
   sp->study();//体现多态性
   return 0;
}
运行结果:
Constructing a student without parameter...
Constructing a student without parameter...
Constructing a student without parameter...
学习高等数学和大学英语。
随便学些什么。
学习语数外。

我们看到,将学习设置为虚函数之后,无论对象指针sp指向哪种学生对象,sp->study()的执行结果总是与对应的类相符合的。多态就通过虚函数实现了。

我们在编写成员函数的时候,可以把尽可能多的成员函数设置为虚函数。这样做可以充分表现多态性,并且也不会给程序带来不良的副作用。

无法实现多态的虚函数

使用虚函数可以实现多态,但是如果在使用虚函数的同时再使用重载,就会可能使虚函数失效。我们修改程序17.7.1,看看重载会给虚函数带来些什么麻烦:(程序17.7.2)
//student.h
#include <iostream>
using namespace std;
class student
{
   public:
   student(char *n,int a,int h,int w);
   student();
   void set(char *n,int a,int h,int w);
   char * sname();
   int sage();
   int sheight();
   int sweight();
   virtual void study(int c=0);//设置为虚函数,带默认参数
   protected:
   char name[10];//姓名
   int age;//年龄
   int height;//身高
   int weight;//体重
};
……
void student::study(int c)
{
   cout <<"随便学些什么。" <<endl;
   return;
}
//undergraduate.h和pupil.h同程序17.7.1
//main.cpp
#include <iostream>
#include "undergraduate.h"
#include "pupil.h"
using namespace std;
int main()
{
   Undergraduate s1;
   student s2;
   Pupil s3;
   student *sp=&s1;
   s1.set("Tom",21,178,60);
   sp->study(1);//带参数
   sp=&s2;
   s2.set("Jon",22,185,68);
   sp->study();
   sp=&s3;
   s3.set("Mike",8,148,45);
   sp->study();
   return 0;
}

运行结果:
Constructing a student without parameter...
Constructing a student without parameter...
Constructing a student without parameter...
随便学些什么。
随便学些什么。
随便学些什么。

当学生类的study成员函数和本科生类的study成员函数参数格式不同时,即使把学生类中的study设置为虚函数,编译器也无法找到本科生类中与之完全相同的study函数。多态是在程序员没有指定调用父类还是某个子类的成员函数时,电脑根据程序员的要求,揣测并选择最合适的成员函数去执行。但是当成员函数的参数格式不同时,程序员在调用成员函数的各种参数无疑就是在暗示到底调用哪个成员函数。这时电脑岂敢自作主张揣测人类的心思?因此,要使用虚函数实现多态性,至少要使各个函数的参数格式也完全相同。


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1551778次
    • 积分:14849
    • 等级:
    • 排名:第767名
    • 原创:198篇
    • 转载:105篇
    • 译文:2篇
    • 评论:280条
    最新评论