这几天在研究C++虚函数表的实现机制,有几个疑问还望大牛指教。
据析,一个C++类有了虚函数就会在只读节区生成一个虚函数表,比如这个类:
class CTest{
public:
virtual ~CTest(){}
virtual void Func1( int a, bool b, unsigned c ){
a += b + c;
}
virtual void Func2( char a ){
a += 10;
}
};
会有这样的一个虚函数表:
CONST SEGMENT
??_7CTest@@6B@ DD FLAT:??_R4CTest@@6B@ ;RTTI
DD FLAT:??_ECTest@@UAEPAXI@Z ;析构函数
DD FLAT:?Func1@CTest@@UAEXH_NI@Z
DD FLAT:?Func2@CTest@@UAEXD@Z
CONST ENDS
其默认生成的构造函数如下:
??0CTest@@QAE@XZ PROC ;CTest::CTest
; _this$ = ecx
push ebp
mov ebp, esp
push ecx
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov DWORD PTR [eax], OFFSET ??_7CTest@@6B@
mov eax, DWORD PTR _this$[ebp]
mov esp, ebp
pop ebp
ret 0
??0CTest@@QAE@XZ ENDP
对于以上的内容,我有几点疑惑:
1.定义虚函数表时,VC是这样定义的
??_7CTest@@6B@ DD FLAT:??_R4CTest@@6B@
FLAT是什么意思?
假如这样定义:
??_7CTest@@6B@ DD OFFSET ??_R4CTest@@6B@
行不行?
两者有什么区别?
2.对于CTest虚函数表,存放了四个地址,后三个是CTest的函数地址,第一个是RTTI结构信息地址,而VC生成CTest的默认构造函数中有这样一条指令
mov DWORD PTR [eax], OFFSET ??_7CTest@@6B@
,这意思是说eax所指内存信息是CTest的RTTI结构信息地址咯,但是我在main函数里有这么一句C++代码
p1->Func1(2, 1, 4); //p1是CTest*类型
对应生成的汇编代码是
push 4
push 1
push 2
mov edx, DWORD PTR _p1$[ebp] ;_p1$[ebp]与C++中的指针p1相对应
mov eax, DWORD PTR [edx]
mov ecx, DWORD PTR _p1$[ebp]
mov edx, DWORD PTR [eax+4]
call edx
据之前分析,mov edx, DWORD PTR [eax+4]这段代码是错的,应该是mov edx, DWORD PTR [eax+8]才对,但VC为什么生成的只是让eax加4呢?
另:对于RTTI的具体实现原理还没弄明白,下一步准备好好研究一下,如果大牛哥好心,顺便给点提示
谢谢!
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课