程序11. #include <iostream>
using namespace std;
class Class {
virtual void f() { cout << "Class::f" << endl; }
virtual void g() { cout << "Class::g" << endl; }
};
int main() {
Class objClass;
cout << "Address of virtual pointer " << (int*)(&objClass+0) << endl;
cout << "Value at virtual pointer i.e. Address of virtual table "
<< (int*)*(int*)(&objClass+0) << endl;
cout << endl << "Information about VTable" << endl << endl;
cout << "Value at 1st entry of VTable "
<< (int*)*((int*)*(int*)(&objClass+0)+0) << endl;
cout << "Value at 2nd entry of VTable "
<< (int*)*((int*)*(int*)(&objClass+0)+1) << endl;
return 0;
}
程序的输出为:
Address of virtual pointer 0012FF7C
Value at virtual pointer i.e. Address of virtual table 0046C0EC
Information about VTable
Value at 1st entry of VTable 0040100A
Value at 2nd entry of VTable 0040129E

那么,有一个问题很自然地出现了——编译器是如何知道虚函数表的长度的呢?答案是:虚函数表的最后一个入口为NULL。你可以把程序改一改来考虑这个问题。
程序12. #include <iostream>
using namespace std;
class Class {
virtual void f() { cout << "Class::f" << endl; }
virtual void g() { cout << "Class::g" << endl; }
};
int main() {
Class objClass;
cout << "Address of virtual pointer " << (int*)(&objClass+0) << endl;
cout << "Value at virtual pointer i.e. Address of virtual table "
<< (int*)*(int*)(&objClass+0) << endl;
cout << endl << "Information about VTable" << endl << endl;
cout << "Value at 1st entry of VTable "
<< (int*)*((int*)*(int*)(&objClass+0)+0) << endl;
cout << "Value at 2nd entry of VTable "
<< (int*)*((int*)*(int*)(&objClass+0)+1) << endl;
cout << "Value at 3rd entry of VTable "
<< (int*)*((int*)*(int*)(&objClass+0)+2) << endl;
cout << "Value at 4th entry of VTable "
<< (int*)*((int*)*(int*)(&objClass+0)+3) << endl;
return 0;
}
程序的输出为:
Address of virtual pointer 0012FF7C
Value at virtual pointer i.e. Address of virtual table 0046C134
Information about VTable
Value at 1st entry of VTable 0040100A
Value at 2nd entry of VTable 0040129E
Value at 3rd entry of VTable 00000000
Value at 4th entry of VTable 73616C43
这个程序的输出示范了虚函数表的最后一个入口为NULL。就让我们来用已有的知识来调用虚函数吧:
程序13. #include <iostream>
using namespace std;
class Class {
virtual void f() { cout << "Class::f" << endl; }
virtual void g() { cout << "Class::g" << endl; }
};
typedef void(*Fun)(void);
int main() {
Class objClass;
Fun pFun = NULL;
// 调用第一个虚函数
pFun = (Fun)*((int*)*(int*)(&objClass+0)+0);
pFun();
// 调用第二个虚函数
pFun = (Fun)*((int*)*(int*)(&objClass+0)+1);
pFun();
return 0;
}
程序的输出为:
Class::f
Class::g
现在我们来看看多重继承的情况。先看一个多重继承最简单的情况:
程序14. #include <iostream>
using namespace std;
class Base1 {
public:
virtual void f() { }
};
class Base2 {
public:
virtual void f() { }
};
class Base3 {
public:
virtual void f() { }
};
class Drive : public Base1, public Base2, public Base3 {
};
int main() {
Drive objDrive;
cout << "Size is = " << sizeof(objDrive) << endl;
return 0;
}
程序的输出为:Size is = 12
这个程序示范了当你从多个基类继承一个类的时候,这个派生类就会拥有所有基类的虚函数表指针。

那么,当派生类也有虚函数表指针的时候会发生什么事情呢?让我们看看下面的程序来弄懂关于带有虚函数的多重继承的概念吧。
程序15. #include <iostream>
using namespace std;
class Base1 {
virtual void f() { cout << "Base1::f" << endl; }
virtual void g() { cout << "Base1::g" << endl; }
};
class Base2 {
virtual void f() { cout << "Base2::f" << endl; }
virtual void g() { cout << "Base2::g" << endl; }
};
class Base3 {
virtual void f() { cout << "Base3::f" << endl; }
virtual void g() { cout << "Base3::g" << endl; }
};
class Drive : public Base1, public Base2, public Base3 {
public:
virtual void fd() { cout << "Drive::fd" << endl; }
virtual void gd() { cout << "Drive::gd" << endl; }
};
typedef void(*Fun)(void);
int main() {
Drive objDrive;
Fun pFun = NULL;
// 调用Base1的第一个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+0);
pFun();
// 调用Base1的第二个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+1);
pFun();
// 调用Base2的第一个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+1)+0);
pFun();
// 调用Base2的第二个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+1)+1);
pFun();
// 调用Base3的第一个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+2)+0);
pFun();
// 调用Base3的第二个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+2)+1);
pFun();
// 调用派生类的第一个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+2);
pFun();
// 调用派生类的第二个虚函数
pFun = (Fun)*((int*)*(int*)((int*)&objDrive+0)+3);
pFun();
return 0;
}
程序的输出为:
Base1::f
Base1::g
Base2::f
Base2::f
Base3::f
Base3::f
Drive::fd
Drive::gd

我们可以通过使用static_cast获得派生类虚函数表指针的偏移量,请看以下程序:
程序16. #include <iostream>
using namespace std;
class Base1 {
public:
virtual void f() { }
};
class Base2 {
public:
virtual void f() { }
};
class Base3 {
public:
virtual void f() { }
};
class Drive : public Base1, public Base2, public Base3 {
};
// 任意的非0值,因为0乘任何数都得0
#define SOME_VALUE 1
int main() {
cout << (DWORD)static_cast<Base1*>((Drive*)SOME_VALUE)-SOME_VALUE << endl;
cout << (DWORD)static_cast<Base2*>((Drive*)SOME_VALUE)-SOME_VALUE << endl;
cout << (DWORD)static_cast<Base3*>((Drive*)SOME_VALUE)-SOME_VALUE << endl;
return 0;
}
ATL使用了一个定义在ATLDEF.H中的宏offsetofclass来做这件事,这个宏被定义为:
#define offsetofclass(base, derived) ((DWORD)(static_cast<base*>((derived*)_ATL_PACKING))-_ATL_PACKING)
这个宏返回了在派生类对象模型中基类虚函数表指针的偏移量,让我们来看看下面这个例子:
程序17. #include <windows.h>
#include <iostream>
using namespace std;