虚函数表的百度解释如下:
类的虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址
注意的是,编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。类的每个虚成员占据虚函数表中的一行。如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。
一个类只要有虚函数,其就会有有一个虚函数表指针,通过指针,编译器可以找到对应的虚函数地址。具体说明见下面的网址
http://apps.hi.baidu.com/share/detail/10397448
此处我想通过一个实验来加强对编译器使用虚函数表的理解。讨论帖见下面的地址
我设计两个类,一个基类和它的派生类,两个类都只有一个虚函数。在百度上查到“类的虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址”。因此我想,如果我将派生类对象覆盖了基类对象,是否会把基类的虚函数表指针更改呢?
测试的代码如下:
有兴趣的可以运行一下。
输出显示a的this指针保存的内容的确发生了变化,但是其仍能寻找的a的print函数。这让我对虚函数表指针产生了疑惑,这个指针明明指向的是b的虚函数表,为啥没有调用b的print函数呢?编译器是如何做到这一点的?
后来使用A *p=&a;p->print();发现此时调用的是B的打印函数。
关于这个疑惑,一个牛人(飞雪)的解释是,多态的条件不满足,意思是只有指针或引用才能产生多态,也就是说p是指针,编译器会进行多态性检测(检查虚函数表)。而a.print()不是指针或引用,并不满足多态条件,那a.print()编译器是如何查找函数地址的呢?这个问题没有人告诉我,还需要等待。。。
经过讨论帖里的回复,终于明白了编译器的调用原则:静态调用和动态调用
a.print() 等价于 a.A::print(); 此处是静态调用,因此此时不会经过虚函数表
p->print() 等价于 a.B::print();此处是动态调用,因此需要查询虚函数表
后来我又做了一个实验加深理解,将B类空间的打印去掉,声明一个C类继承B类,如上述操作后,调用b.print(),我想此处B::print是不存在的,它是否会去找虚函数表呢?证明结果不会,因此静态调用应该是当B类空间找不到时,会到B类的基类中寻找,即b.B::A::print()。只有动态调用才会找虚函数表,而满足动态调用的条件是指针或引用。