1. 为什么要使用虚函数?
- 在C++语言中,基类将类型相关的函数与派生类不做改变直接继承的函数区分对待,对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明成虚函数。
- 虚函数使用virtual关键字表示,如下面程序中Parent类中的Function2就是虚函数
- 基类中定义的虚函数必须在其派生类中有和其完全相同的函数声明(函数名、参数列表,返回值),使用override关键字,表示该函数可能会覆盖基类中对应的虚函数。
2. 看一个例子
#include <iostream>
using namespace std;
class Parent
{
public:
char data[20];
void Function1(){
printf("This is parent,function1\n");
}
// 这里声明Function2是虚函数
virtual void Function2() {
printf("This is parent,function2\n");
}
}parent;
class Child:public Parent
{
void Function1(){
printf("This is child,function1\n");
}
void Function2() override{
printf("This is child,function2\n");
}
}child;
int main(int argc, char* argv[])
{
int a;
Parent *p; // 定义一个基类指针
scanf("%d",&a);
if(a) // 如果输入1
p=&child; // 指向继承类对象
else // 如果输入0
p=&parent; // 否则指向基类对象
p->Function1(); // 这里在编译时会直接给出Parent::Function1()的入口地址。
p->Function2(); // 注意这里,执行的是哪一个Function2?
return 0;
}
- 运行结果:
- 如果键盘输入1,则:
1
This is parent,function1
This is child,function2
- 如果键盘输入0,则:
0
This is parent,function1
This is parent,function2
一句概括的话:当使用基类的指针或引用调用虚函数时,会发生动态绑定,即该指针或引用可能绑定一个基类对象,也可能绑定一个派生类对象,这在函数运行的时候才能得知。如果绑定的是基类对象,则调用的是基类中的虚函数,如果绑定的是派生类对象,则调用的是派生类中的虚函数。
3. 动态绑定和静态绑定的概念
- 静态绑定:对于引用或指针,在编译过程中就已经绑定了对象;
- 动态绑定:对于引用或指针,在编译过程中不知道该绑定什么对象,只有在运行时才进行绑定;
注意:当且仅当基类的指针或引用调用虚函数时,才会发生动态绑定
成员函数如果没有被声明为虚函数,则其解析过程发生在编译时而非运行时。–《C++ Primer》
- 因此,上面主程序中
p->Function1();
调用Function1时,由于Function1不是虚函数,因此不会发生动态绑定。在编译时,p就已经绑定了其声明的对象Parent类,因此其调用的自然是Parent中的Function1; p->Function2();
调用Function2时,由于Function2是虚函数,因此发生动态绑定。编译时,p不会绑定任何对象,在运行时,如果p绑定了Parent对象,则调用的是Parent类中的虚函数,如果绑定了Child对象,则调用的是Child类中的虚函数;