程序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;
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 { };
#define _ATL_PACKING 8
#define offsetofclass(base, derived) \ ((DWORD)(static_cast<base*>((derived*)_ATL_PACKING))-_ATL_PACKING)
int main() { cout << offsetofclass(Base1, Drive) << endl; cout << offsetofclass(Base2, Drive) << endl; cout << offsetofclass(Base3, Drive) << endl; return 0; } |
派生类的内存布局为:
程序的输出为:
这个程序的输出示范了这个宏返回指定基类的虚函数表指针偏移量。在Don Box的《COM本质论》中,它使用了一个简单的的宏,你可以修改这个程序,用Box的宏替换ATL的宏。
程序18.
#include <windows.h> #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 { };
#define BASE_OFFSET(ClassName, BaseName) \ (DWORD(static_cast<BaseName*>(reinterpret_cast<ClassName*>\ (0x10000000))) - 0x10000000)
int main() { cout << BASE_OFFSET(Drive, Base1) << endl; cout << BASE_OFFSET(Drive, Base2) << endl; cout << BASE_OFFSET(Drive, Base3) << endl; return 0; } |
这一程序的目的和输出与前一个程序完全相同。
现在让我们用这个宏来做些特别的东西,事实上我们可以通过获得派生类内存结构中基类虚函数表指针的偏移量的方法来调用指定基类中的虚函数。
程序19.
#include <windows.h> #include <iostream> using namespace std;
class Base1 { public: virtual void f() { cout << "Base1::f()" << endl; } };
class Base2 { public: virtual void f() { cout << "Base2::f()" << endl; } };
class Base3 { public: virtual void f() { cout << "Base3::f()" << endl; } };
class Drive : public Base1, public Base2, public Base3 { };
#define _ATL_PACKING 8
#define offsetofclass(base, derived) \ ((DWORD)(static_cast<base*>((derived*)_ATL_PACKING))-_ATL_PACKING)
int main() { Drive d;
void* pVoid = NULL;
// 调用Base1的函数 pVoid = (char*)&d + offsetofclass(Base1, Drive); ((Base1*)(pVoid))->f();
// 调用Base2的函数 pVoid = (char*)&d + offsetofclass(Base2, Drive); ((Base2*)(pVoid))->f();
// 调用Base3的函数(译注:原文为Base1) pVoid = (char*)&d + offsetofclass(Base3, Drive); ((Base3*)(pVoid))->f();
return 0; } |
程序的输出为:
Base1::f() Base2::f() Base3::f() |
在本章教程中,我尝试着解释了ATL中offsetofclass宏的工作方式。我希望在下一篇文章中,继续探究ATL中其它的秘密。
|