- 论坛徽章:
- 2
|
为什么大家都喜欢干这种事 …… 这是严重依赖编译器的,C++标准甚至都没要求是要用虚函数表来实现虚函数机制。
gcc3.4.x 是通过给参数列表增添一个隐藏参数, 来传递this的, 代码 :
/*----------------------------------------------------------------------------*/
class C {
int i_;
public:
explicit C(int i) :i_(i) {}
virtual ~C() {}
virtual void f() { printf("C::f(%d)\n",i_); }
};
#if defined(__GNUC__)
#if __GNUC__!=3
#error not test on other gcc version except gcc3.4
#endif
#include <assert.h>
#include <string.h>
#include <stdint.h>
int main()
{
C c1(1212);
C c2(326);
typedef void (* virtual_function)(C*);
// gcc 通过一个增加一个额外参数, 传递this
// virtual_function 即是C的虚函数签名
struct
{
virtual_function* vptr;
// 虚函数表指针
// 当然,它指向的表不全是函数, 还有RTTI信息
// 总之, 它就是这个类的标识, 唯一的“类型域”
int i;
// data member
} caster;
// 我们猜想, gcc将虚函数表指针安排在对象的最前面。
memcpy(&caster,&c1,sizeof(caster));
printf("c1.i_ = %d\n",caster.i); // 1212
printf("c1.vptr_ = %p\n"
,reinterpret_cast<void*>(reinterpret_cast<intptr_t>(caster.vptr)) );
virtual_function* vptr1 = caster.vptr;
memcpy(&caster,&c2,sizeof(caster));
printf("c2.i_ = %d\n",caster.i);
printf("c2.vptr_ = %p\n",(void*)caster.vptr);
virtual_function* vptr2 = caster.vptr;
assert(vptr1==vptr2);
// 显然, 它们都是C, 所以vptr指向相同的地址
vptr1[2](&c1); // C::f(1212)
vptr2[2](&c2); // C::f(326)
// 我们再猜想 f在虚函数表中的第2项
}
#elif defined(_MSC_VER)
#include <assert.h>
#include <stddef.h>
#include <string.h>
int main()
{
C c1(1212);
C c2(326);
typedef void (__stdcall* virtual_function)(void);
// msvc 通过ecx传递this, 所以参数列表和虚函数相同
// 同时, msvc生成的虚函数, 会平衡堆栈
// 所以这里使用 __stdcall 让调用者不做堆栈的平衡工作
struct {
virtual_function* vptr;
int i;
} caster;
// 这同样是对编译器生成代码的一种假设和依赖
memcpy(&caster,&c1,sizeof(caster));
printf("c1.i_ = %d\n",caster.i); // 1212
virtual_function* vptr1 = caster.vptr;
printf("c1.vptr_ = %p\n"
,reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(vptr1)) );
memcpy(&caster,&c2,sizeof(caster));
printf("c2.i_ = %d\n",caster.i); // 326
virtual_function* vptr2 = caster.vptr;
printf("c2.vptr_ = %p\n"
,reinterpret_cast<void*>(reinterpret_cast<ptrdiff_t>(vptr2)) );
assert(vptr1==vptr2);
// 显然 c1 c2 都是 C,它们的虚指针是相同的
// 但是, 直接调用是不行的, 因为没传递this
//vptr1[2]();
// 这样也不行
//_asm { lea ecx, c1 }
// 因为下面这行代码, 修改了 ecx
// vptr1[2]();
// 所以要如下进行直接调用
virtual_function f1 = vptr1[1];
_asm {
lea ecx,c1
call f1
}
virtual_function f2 = vptr2[1];
_asm {
lea ecx,c2
call f2
}
// 分别打印出 C::f(1212),C::f(326)
// 同时, C::f在虚表的第1项, vs的watch窗口说的 ……
}
#else
#error unknown compiler
#endif
/*----------------------------------------------------------------------------*/
测试的编译器:
gcc (GCC) 3.4.2 (mingw-special)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.42 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
[ 本帖最后由 OwnWaterloo 于 2009-4-19 22:05 编辑 ] |
|