①虚函数是重载的另一种表现形式。
②是一种动态的重载方式,它提供了一种更为灵活的运行时的多态性机制。
③虚函数允许函数调用与函数体之间的联系在运行时才建立,即在运行时才决定如何动作,即所谓的动态联编。
例 1:虚函数的引例。
#include<iostream>
using namespace std;
class My_base{ //声明基类为 My_base
private:
int a,b;
public:
My_base(int x,int y){ //基类构造函数
a=x;b=y;
}
void show(){ //基类的成员函数 show()
cout<<"调用基类 My_base 的 show() 函数。"<<endl;
cout<<a<<" "<<b<<endl;
}
};
class My_class:public My_base{ //声明派生类 My_class
private:
int c;
public:
My_class(int x,int y,int z):My_base(x,y){
c=z;
}
void show(){
cout<<" 调用派生类 My_class 的 show() 函数\n";
cout<<"c="<<c<<endl;
}
};
int main(){
My_base mb(50,50),*mp; //定义基类对象 mb 和对象指针 mp
My_class mc(10,20,30); //定义派生类对象 mc
mp=&mb; //对象指针 mp 指向基类对象 mb
mp->show();
mp=&mc; //对象指针 mp 指向派生类对象 mc
mp->show();
return 0;
}
执行结果:
其中,执行语句
mp=&mc; //对象指针 mp 指向派生类对象 mc
虽然指针 mp 指向了派生类对象 mc,但是执行语句 “mp->show();” 后,调用的不是派生类的成员函数 show ,而仍然是基类的同名成员函数 show,为什么?
答:在 C++ 中规定:基类的对象指针 可以指向它的公有派生的对象,但是当其指向公有派生类对象时,它只能访问派生类中从基类继承来的成员,而不能访问公有派生类中定义的成员。例如:
class A{
···
public:
void print1();
};
class B:public A{
···
public:
void print2();
};
int main(){
A op1,*ptr; //定义基类 A 的对象 op1 和基类指针 ptr
B op2; //定义派生类 B 的对象 op2
ptr=&op1; //将指针 ptr 指向基类对象 op1
ptr->print1(); //调用基类函数 print1()
ptr=&op2; //将指针 ptr 指向派生类对象 op2
ptr->print1();
//正确,可以调用对象 op2 从其基类继承来的成员函数 print1()
ptr->print2();
//错误,基类指针 ptr 不能访问派生类中定义的成员函数 print2()
return 0;
}
在例 1 中使用对象指针的目的是为了表达一种动态的性质,即当指针指向不同对象 (基类对象或派生类对象)时,分别调用不同类的成员函数。如果将函数说明为虚函数,就能实现这种动态调用的功能。
例 2:虚函数的引入
将 例 1 中的函数 show() 定义为虚函数(即在函数定义前加 virtual) ,以实现动态调用的功能:
···
virtual void show(){ //在基类中定义虚函数 show()
cout<<"调用基类 My_base 的 show() 函数。"<<endl;
cout<<a<<" "<<b<<endl;
}
···
执行结果:
把基类中的 show 函数定义为虚函数时,程序的运行结果就正确了,这是因为关键字 virtual 指示 C++ 编译器,函数调用 mp->show 要在运行时确定所要调用的函数,即要对该调用进行动态联编。因此,程序在运行时根据指针 mp 所指向的实际对象,调用该对象的成员函数。
把使用同一种调用形式“ mp->show() ” ,调用同一类族中不同类的虚函数称为动态的多态性。可见,虚函数可使 C++ 支持运行时的多态性。