多态能够方便我们编写程序,可以让不同的类与它独特的成员函数一一对应。即使我们只是简单地“称呼”,程序也会很明白我们的心思。那么,多态应该如何实现呢?
在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函数。多态是在程序员没有指定调用父类还是某个子类的成员函数时,电脑根据程序员的要求,揣测并选择最合适的成员函数去执行。但是当成员函数的参数格式不同时,程序员在调用成员函数的各种参数无疑就是在暗示到底调用哪个成员函数。这时电脑岂敢自作主张揣测人类的心思?因此,要使用虚函数实现多态性,至少要使各个函数的参数格式也完全相同。