C++中的虚函数(表)实现机制以及用C语言对其进行的模拟实现(三)
, 毕竟继承类不管你如何继承, 都不会影响到基类, 对吧?
?
? ? 接下来看看调用反汇编代码:
?
复制代码
1 ? ? pd1->derive1_fun1();
2 00825466 ?mov ? ? ? ? eax,dword ptr [pd1] ?
3 00825469 ?mov ? ? ? ? edx,dword ptr [eax] ?
4 0082546B ?mov ? ? ? ? esi,esp ?
5 0082546D ?mov ? ? ? ? ecx,dword ptr [pd1] ?
6 00825470 ?mov ? ? ? ? eax,dword ptr [edx+8] ?
7 00825473 ?call ? ? ? ?eax ?
复制代码
?
?
? ? 汇编代码解释:
? ? ? ? ? 第2行: 由于pd1是指向d1的指针, 所以执行此句后 eax 就是d1的地址?
? ? ? ? ? 第3行: 又因为Base1::__vfptr是Base1的第1个成员, 同时也是Derive1的第1个成员, 那么: &__vfptr == &d1, ?clear??
? ? ? ? ? ? ? ? ? ? 所以当执行完 ?mov edx, dword ptr[eax] 后, edx就得到了__vfptr的值, 也就是虚函数表的地址.
? ? ? ? ? 第5行: 由于是__thiscall调用, 所以把this保存到ecx中.
? ? ? ? ? 第6行: 一定要注意到那个edx+8, 由于edx是虚函数表的地址, 那么 edx+8将是虚函数表的第3个元素, 也就是__vftable[2]!!!
? ? ? ? ? 第7行: 调用虚函数.
?
? ? ?结果:?
? ? ? ? ? ?1. 现在我们应该知道内幕了! 继承类Derive1的虚函数表被加在基类的后面! 事实的确就是这样!
? ? ? ? ? ?2. 由于Base1只知道自己的两个虚函数索引[0][1], 所以就算在后面加上了[2], Base1根本不知情, 不会对她造成任何影响.
? ? ? ? ? ?3. 如果基类没有虚函数呢? 这个问题我们留到第9小节再来讨论!
?
? ? ?最新的类对象布局表示:
? ? ? ? ? ? ??
?
? ? ?8. 多继承且存在虚函数覆盖同时又存在自身定义的虚函数的类对象布局
?
? ? ? ? 真快, 该看看多继承了, 多继承很常见, 特别是接口类中!
? ? ? ? 依然写点小类玩玩:
?
复制代码
?1 class Base1
?2 {
?3 public:
?4 ? ? int base1_1;
?5 ? ? int base1_2;
?6?
?7 ? ? virtual void base1_fun1() {}
?8 ? ? virtual void base1_fun2() {}
?9 };
10?
11 class Base2
12 {
13 public:
14 ? ? int base2_1;
15 ? ? int base2_2;
16?
17 ? ? virtual void base2_fun1() {}
18 ? ? virtual void base2_fun2() {}
19 };
20?
21 // 多继承
22 class Derive1 : public Base1, public Base2
23 {
24 public:
25 ? ? int derive1_1;
26 ? ? int derive1_2;
27?
28 ? ? // 基类虚函数覆盖
29 ? ? virtual void base1_fun1() {}
30 ? ? virtual void base2_fun2() {}
31?
32 ? ? // 自身定义的虚函数
33 ? ? virtual void derive1_fun1() {}
34 ? ? virtual void derive1_fun2() {}
35 };
复制代码
?
?
? ? ? 代码变得越来越长啦! 为了代码结构清晰, 我尽量简化定义.
? ? ? 初步了解一下对象大小及偏移信息:
? ? ? ? ??
?
? ? 貌似, 若有所思? 不管, 来看看VS再想:
? ? ? ? ??
?
? ? 哇, 不摆了! 一丝不挂啊! :-)
? ? 结论:
? ? ? ? ? ?1. 按照基类的声明顺序, 基类的成员依次分布在继承中.
? ? ? ? ? ?2. 注意被我高亮的那两行, 已经发生了虚函数覆盖!
? ? ? ? ? ?3. 我们自己定义的虚函数呢? 怎么还是看不见?!
?
? ? 好吧, 继承反汇编, 这次的调用代码如下:
?
1 Derive1 d1;
2 Derive1* pd1 = &d1;
3 pd1->derive1_fun2();
?
?
? ? 反汇编代码如下:
?
复制代码
1 ? ? pd1->derive1_fun2();
2 00995306 ?mov ? ? ? ? eax,dword ptr [pd1] ?
3 00995309 ?mov ? ? ? ? edx,dword ptr [eax] ?
4 0099530B ?mov ? ? ? ? esi,esp ?
5 0099530D ?mov ? ? ? ? ecx,dword ptr [pd1] ?
6 00995310 ?mov ? ? ? ? eax,dword ptr [edx+0Ch] ?
7 00995313 ?call ? ? ? ?eax ?
复制代码
?
?
? ? ? 解释下, 其实差不多:
? ? ? ? ? ? 第2行: 取d1的地址
? ? ? ? ? ? 第3行: 取Base1::__vfptr的值!!
? ? ? ? ? ? 第6行: 0x0C, 也就是第4个元素(下标为[3])
? ? ? 结论:
? ? ? ? ? ? Derive1的虚函数表依然是保存到第1个拥有虚函数表的那个基类的后面的.
?
? ? ?看看现在的类对象布局图:
? ? ? ? ??
?
?
?
? ? ? 如果第1个基类没有虚函数表呢? 进入第9节!
?
? ? ?9. 如果第1个直接基类没有虚函数(表)
?
? ? ? ? 这次的代码应该比上一个要稍微简单一些, 因为把第1个类的虚函数给去掉鸟!
?
复制代码
?1 class Base1
?2 {
?3 public:
?4 ? ? int base1_1;
?5 ? ? int base1_2;
?6 };
?7?
?8 class Base2
?9 {
10 public:
11 ? ? int base2_1;
12 ? ? int base2_2;
13?
14 ? ? virtual void base2_fun1() {}
15 ? ? virtual void base2_fun2() {}
16 };
17?
18 // 多继承
19 class Derive1 : public Base1, public Base2
20 {
21 public:
22 ? ? int derive1_1;
23 ? ? int derive1_2;
24?
25 ? ? // 自身定义的虚函数
26 ? ? virtual void derive1_fun1() {}
27 ? ? virtual void derive1_fun2() {}
28 };
复制代码
?
?
? ? ?来看看VS的布局:
? ? ? ? ?
?
? ?这次相对前