有虚函数的类都有一个虚函数表,它是实现多态的关键。
虚函数表可以继承,如果子类没有重写虚函数,那么子类虚函数表中仍然会有该函数的地址,只不过这个地址指向的是基类的函数实现。如果子类重写了相应的虚函数,那么虚函数表中的地址就会改变,指向自身的函数实现。如果派生类中有自己的虚函数,那么虚函数表中会添加该项。
每个对象调用的虚函数都是通过虚函数表 指针来索引的,因此虚函数表指针的正确初始化是非常重要的。虚函数表指针应该在什么时候、什么地方初始化呢?
应该在构造函数中进行虚函数表的创建和虚函数表指针的初始化。根据构造函数的调用顺序,在构造子类对象时,要先调用父类的构造函数,此时编译器只看到了父类,并不知道后面是否有继承者,所以它只初始化父类对象的虚函数表指针,该虚函数表指针指向父类的虚函数表;当执行子类的构造函数时,子类对象的虚函数表指针被初始化,指向自身的虚函数表。
#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.