对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针
如下面代码:
#include <iostream>
using namespace std;
class B
{
public:
B()
{
_b = 10;
cout << "this = " << this << endl;
}
virtual void test1()
{
cout << "B::test1()" <<endl;
}
virtual void test2()
{
cout << "B::test2()" << endl;
}
virtual void test3()
{
cout << "B::test3()" << endl;
}
private:
int _b;
};
int main()
{
B b;
return 0;
}
通过对地址进行跟踪:

在vs2013中虚函数地址以全0结束;
既然,只要类中声明虚函数,在创建对象时就会有一个指针指向此类的虚表位置,那么在类的继承体系中,基类中的虚表是如何被继承的呢?
而且,虚表中存放虚函数的地址,那么我们是否可以用一个函数指针不同类去调用它呢?
因此,为了验证虚表中存放的确实是虚函数的地址,我们不用类去调用,用一个函数指针来调用:
下面代码有三个虚函数,分别输出不同结果:
#include <iostream>
using namespace std;
class B
{
public:
B()
{
_b = 10;
}
virtual void test1()
{
cout << "B::test1()" <<endl;
}
virtual void test2()
{
cout << "B::test2()" << endl;
}
virtual void test3()
{
cout << "B::test3()" << endl;
}
private:
int _b;
};
typedef void(*FUN_TEST)();
int main()
{
B b;
for (int i = 0; i < 3; ++i)
{
FUN_TEST fun = (FUN_TEST)(*((int*)*(int*)&b + i));
fun();
cout << ":" << (int*)fun << endl;
}
system("pause");
return 0;
}

结果:

我们已经知道,在类中只要有虚函数,编译器就会为此类提供一个保存虚函数地址的虚表,那么,在类的继承体系中,这个虚表是如何被继承下来的呢???
1、没有覆盖:
我们用上面同样的方法,用函数指针来调用派生类中的虚函数
class B
{
public:
B()
{
_b = 10;
}
virtual void test1()
{
cout << "B::test1()" <<endl;
}
virtual void test2()
{
cout << "B::test2()" << endl;
}
virtual void test3()
{
cout << "B::test3()" << endl;
}
private:
int _b;
};
class D :public B
{
public:
virtual void test4()
{
cout << "B::test4()" << endl;
}
virtual void test5()
{
cout << "B::test5()" << endl;
}
virtual void test6()
{
cout << "B::test6()" << endl;
}
private:
int _d;
};
typedef void(*FUN_TEST)();
int main()
{
D d;
cout << sizeof(d) << endl;
for (int i = 0; i < 6; ++i)
{
FUN_TEST fun = (FUN_TEST)(*((int*)*(int*)&d + i));
fun();
cout << ":" << (int*)fun << endl;
}
system("pause");
return 0;
}
结果:

分析:在计算派生类大小为12,而非16。说明在继承时基类和派生类的两个虚表合并了,而输出结果也是先输出基类的虚函数,再输出派生类的虚函数,即在新合并的派生类虚表中对应的模型应该是下面这种:

总结:
1、虚函数按照其声明顺序存在于虚表中
2、在派生类中,前面是基类的虚函数,后面是派生类的虚函数。
2、有覆盖
即在派生类中将基类的虚函数进行重写。
前面代码稍加改造:
class B
{
public:
B()
{
_b = 10;
}
virtual void test1()
{
cout << "B::test1()" <<endl;
}
virtual void test2()
{
cout << "B::test2()" << endl;
}
virtual void test3()
{
cout << "B::test3()" << endl;
}
private:
int _b;
};
class D :public B
{
public:
void test1()
{
cout << "D::test1()" << endl;
}
virtual void test5()
{
cout << "D::test5()" << endl;
}
virtual void test6()
{
cout << "D::test6()" << endl;
}
private:
int _d;
};
typedef void(*FUN_TEST)();
int main()
{
D d;
cout << sizeof(d) << endl;
for (int i = 0; i < 5; ++i)
{
FUN_TEST fun = (FUN_TEST)(*((int*)*(int*)&d + i));
fun();
cout << ":" << (int*)fun << endl;
}
system("pause");
return 0;
}
结果:

分析不难得到:派生类的虚表生成是先拷贝基类的虚函数表;如果派生类重写了基类的某个虚函数,就替换虚表中同位置的基类虚函数。然后跟上自己定义的虚函数。