C++虚函数与多态

###为什么需要虚函数?

假如存在以下类层次关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class GF{
public:
void toString(){
cout << "Grandfather class" << endl;
}
};
class Ft : public GF{
public:
void toString(){
cout << "Father class" << endl;
}
};
class Son :public Ft{
public:
void toString(){
cout << "Son class" << endl;
}
};
存在函数`foo(GF *)`,作用是处理类型为`GF*`的实例(instance):
1
2
3
void foo(GF * who){
who->toString();
}
实例化Ft类和Son类:
1
2
3
Ft * father = new Ft();
Son * son = new Son();
//以上声明也可以为 GF * father = new Ft(); 和 GF * son = new Son();
当我们调用:
1
2
foo(son);
foo(father);
自然地,我们希望的运行结果为:
1
2
Son class
Father class
然而以上代码的运行结果为:
1
2
Grandfather class
Grandfather class
可见,在foo(GF * who) 函数中,who-&gt;toString()指向的始终是 GF::toString()。即是说,在调用foo(son)时,son 指针被隐式提升为了GF * 类型。

另一种声明:
1
GF * son = new Son();
虽然调用的是Son 的构造函数,但是son的类型同样被提升为了GF*。因此,调用foo(GF * who)时,其运行表现同上。

此时,为了达到目的,我们可以:
  1. 重载foo(),定义Ft和Son的版本。显然,在GF-Ft-Son的继承链上再派生出新的子类时,需要增加foo()的重载版本。
  2. 引入virtual关键字,将基类的定义改为:
1
2
3
4
5
6
class GF{
public:
virtual void toString(){
cout << "Grandfather class" << endl;
}
};
这样不需要修改foo()函数,即只需保证foo(GF*)的参数类型为祖先类即可保证调用的函数是实例所指向的类中的版本。

一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被称为虚函数。

类中的虚函数是被继承的,即在其派生类中,该函数也是虚的。