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++ 多态和虚函数

  • 2009-04-15 09:21
  • 725KB
  • 下载

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

  • 2010-10-25 20:06
  • 686KB
  • 下载

C++虚函数实现多态

C++是通过虚函数来解决多态问题的 虚函数,就是在基类声明函数是虚拟的,并不是实际存在的函数,然后再派生类中才正式定义此函数。在程序运行期间,用指针指向某一派生类对象,这样就能调用指针指向的派生...

C++ 多态,虚函数

虚函数和多态   1.基本概念 如果在类中某个成员函数被定义为虚函数,那么在该类的派送类中如果拥有与基类虚函数函数原型相同的函数同样为虚函数,并且与其基类中的版本构成覆盖关系。     一个指向子类的...

【步兵 c++】 多态&虚函数

【步兵 c++】 多态&虚函数 by EOS. 多态和虚函数 其实,多态的主要表现其实就是通过父类来调子类。而这种表现的主角就是虚函数。 至于多态和虚函数的具体概念,请自行搜索,我这里就不多说了。 ...

C++多态与虚函数

面向对象程序设计精髓在于继承与多态,为了实现这两功能C++提供了一系列方法,本文介绍多态实现方法。 简单定义一个基类: class Base{ public: void fun(){ cou...

C++中为什么要用虚函数、指针或引用才能实现多态?

/************************************ *多态的经典例子 *2013-11-02 ************************************/ #in...

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

C++的三大特性:封装、继承、多态。以前学的时候自己没去总结,记得在一本c++入门的书讲得还是比较清楚。今天上网找了一下多态,找到下面这篇文章写得比较清晰。 http://pcedu.pconlin...

C++ 多态,虚函数

在c++中可以用基类指针指向其派生类,如果一个派生类继承多个父类,那么如何通过基类指针访问同名的成员呢?         如果基类A,基类B中都有函数fun(),派生类在同时继承A、B后在内存中根据...
  • my_et
  • my_et
  • 2013-07-01 15:26
  • 331
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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