有虚函数的类都有一个虚函数表,它是实现多态的关键。
虚函数表可以继承,如果子类没有重写虚函数,那么子类虚函数表中仍然会有该函数的地址,只不过这个地址指向的是基类的函数实现。如果子类重写了相应的虚函数,那么虚函数表中的地址就会改变,指向自身的函数实现。如果派生类中有自己的虚函数,那么虚函数表中会添加该项。
每个对象调用的虚函数都是通过虚函数表 指针来索引的,因此虚函数表指针的正确初始化是非常重要的。虚函数表指针应该在什么时候、什么地方初始化呢?
应该在构造函数中进行虚函数表的创建和虚函数表指针的初始化。根据构造函数的调用顺序,在构造子类对象时,要先调用父类的构造函数,此时编译器只看到了父类,并不知道后面是否有继承者,所以它只初始化父类对象的虚函数表指针,该虚函数表指针指向父类的虚函数表;当执行子类的构造函数时,子类对象的虚函数表指针被初始化,指向自身的虚函数表。
#include <iostream> using namespace std; class A { public: virtual void f() { cout << "f in A." << endl; } virtual void g() { cout << "g in A." << endl; } void h() { cout << "h in A." << endl; } }; class B : public A { void f() { cout << "f in B." << endl; } }; int main () { cout << "sizeof(A):" << sizeof(A) << endl; cout << "sizeof(B):" << sizeof(B) << endl; A *p = NULL; B b; p = &b; p->f(); p->g(); p->h(); return 0; }
上面的代码中,当B类的对象b构造完成后,其内部的虚函数表指针被初始化为指向B类的虚函数表。
类的成员函数不占用类的空间,它们存储在另外的一个地方;只要类中有虚函数,这个类就会有一个对应的虚函数表,而类会增加一个指向这个虚函数表的指针,B继承了一个类A,所以需要一个指向虚函数表的指针,32位系统下指针的大小为4字节。如果继承了N个类,则对应需要N个指针,分别指向对应的虚函数表。
[root@zhuliting ~]# g++ -o test test.cpp [root@zhuliting ~]# ./test sizeof(A):4 sizeof(B):4 f in B. g in A. h in A.