C++里有虚函数的概念,用法也很简单,在函数声明之前加关键字virtual。之所以有这样的概念的原因,是为了面向对象的程序能“向后兼容”,通过它你可以事先写好程序框架,后面再慢慢实现,而不用改原来的款价。看个例子:
- #include <stdio.h>
- class Base {
- public:
- virtual void foo() {
- printf("virtual Base::foo()./n");
- }
- };
- class Derive : Base {
- public:
- void foo() {
- printf("virtual Derive::foo()./n");
- }
- };
- void bar(Base * pbs)
- {
- pbs->foo();
- }
- int main()
- {
- Base bs;
- bs.foo();
- Derive dr;
- dr.foo();
- bar(&bs)
- return 0;
- }
Derive类重定义了Base类的虚函数foo(),main函数开始的两个函数调用实际上可以看作是对两个普通类的调用,可以说和普通的成员函数调用没任何区别。但是,当调用函数bar()时,虚函数的优点就出来了,虚函数的规则允许父子类对象之间的指针互相赋值,而且,会根据具体执行的时候看指针的具体指向来决定到底执行哪一个foo(),这样我们可以在还没有Derive类的时候就写好整个应用程序框架,以达到向后兼容的目的。
不过这里要说的不是这个,而是,对编译器而言,问题就来了,既然你的规则定的这么灵活,那我就没办法在编译的时候决定到底是调用哪一个foo()了,确切的说是无法确定在代码区的偏移。无奈之下,提出了vtable的概念,vtable的作用就是,允许程序在运行的到bar()这一句的时候决定到底执行那一个foo()。这里我要强调的是顺序问题,也就是先有“向后兼容”,再有虚函数,最后才有vtable、晚绑定等概念,说白了就是由需求决定的,而不是凭空出来这么多些规则。
实际上,虚函数有点像c里的回调函数,如果把函数名看作是普通的指针,回调函数就很好理解了,只不过这里的指针是函数的入口地址,貌似有点特别。顺着这个思路下去,纯虚函数、虚基类也很好理解了。