C++多态和虚函数

原创 2013年12月05日 17:40:38

参考:微学院 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函数。多态是在程序员没有指定调用父类还是某个子类的成员函数时,电脑根据程序员的要求,揣测并选择最合适的成员函数去执行。但是当成员函数的参数格式不同时,程序员在调用成员函数的各种参数无疑就是在暗示到底调用哪个成员函数。这时电脑岂敢自作主张揣测人类的心思?因此,要使用虚函数实现多态性,至少要使各个函数的参数格式也完全相同。


C++ 多重继承 虚继承 虚函数表 多态

C++中的多重继承和虚继承是一个非常重要的概念,也是看你是不是懂C++的一个重要的标志之一。这中间包括了运行时多态,虚函数表等等相关概念。 多重继承,顾名思义,是一个类继承了多个父类。例如class...
  • Troy_Wu
  • Troy_Wu
  • 2016年03月25日 15:05
  • 1945

C语言面向对象编程(三):虚函数与多态

提供了一个非常逼真的虚函数实现,高仿 C++ 中的虚函数表(VTABLE)
  • foruok
  • foruok
  • 2014年01月18日 00:01
  • 11987

C++学习之多态篇(虚函数和虚析构函数的实现原理--虚函数表)

通过下面的代码来说明: #include #include #include using namespace std; /**  *  定义动物类:Animal  *  成员函数:eat()...
  • hudfang
  • hudfang
  • 2016年01月26日 18:13
  • 1669

C++ 虚函数与多态 教学PPT

  • 2010年10月25日 20:06
  • 687KB
  • 下载

C++虚函数和多态学习笔记

  • 2007年08月31日 13:31
  • 37KB
  • 下载

C++中类的多态和虚函数的使用

C++的三大特性:封装、继承和多态。   (一)这里主要讨论多态:   类的多态特性是支持面向对象的语言最主要的特性,有过非面向对象语言开发经历的人,通常对这一章节的内容会觉得不习惯,因为很多...
  • yx20130919
  • yx20130919
  • 2016年06月15日 17:00
  • 129

C++中对纯虚函数和多态的理解

    抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层。       ⑴抽象类的定义:       称带有纯虚函数的类为抽象类。     ...
  • zhanghuaichao
  • zhanghuaichao
  • 2016年10月10日 16:11
  • 722

<C++略识>之多态和虚函数

1. 什么是多态?** 书本上关于多态的解释:指相同对象收到不同消息或不同对象收到相同消息时产生不同的动作。 ** VincentCZW在《C++中虚函数和多态》中的解释:关于多态,简而言之就是用父...
  • u013003827
  • u013003827
  • 2016年07月22日 23:09
  • 129

c++远征之多态篇——纯虚函数和抽象类、接口类

以下内容源于慕课网的学习整理,如有侵权,请告知删除。 1、纯虚函数 没有函数体;=0;即只有函数声明,而没有函数定义的虚函数,是纯虚函数。 2、抽象类 ...
  • oqqHuTu12345678
  • oqqHuTu12345678
  • 2017年06月16日 18:38
  • 174

C++重载、多态、虚函数

另外一篇关于C++重载、多态、虚函数的文章: [C++基础]重载、覆盖、多态与函数隐藏(1) [C++基础]重载、覆盖、多态与函数隐藏(2) [C++基础]重载、覆盖、多态与...
  • zimingjushi
  • zimingjushi
  • 2011年10月24日 19:02
  • 1051
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++多态和虚函数
举报原因:
原因补充:

(最多只允许输入30个字)