多重继承多态的实现

来源:互联网 发布:sem优化方案模板 编辑:程序博客网 时间:2024/06/10 00:24
关于C++的虚表以及C++对象的内存布局,这两篇blog写的不错:

C++虚函数表解析
http://blog.csdn.net/haoel/archive/2007/12/18/1948051.aspx

C++对象的内存布局(上)
http://blog.csdn.net/haoel/archive/2008/10/15/3081328.aspx
C++对象的内存布局(下)
http://blog.csdn.net/haoel/archive/2008/10/15/3081385.aspx

单重继承虚函数实现多态的机制比较简单,从对象首地址取虚表指针就可以从虚表动态访问到相应的虚函数,但多重继承有多个父类的虚表指针,怎么实现动态绑定呢?
借用上面几篇blog中的例子:
  1. class Base1
  2. {
  3.   public:
  4.     int ibase1;
  5.     Base1():ibase1(10) {}
  6.     virtual void f() { cout << "Base1::f()" << endl; }
  7.     virtual void g() { cout << "Base1::g()" << endl; }
  8.     virtual void h() { cout << "Base1::h()" << endl; }
  9. };
  10. class Base2
  11. {
  12.   public:
  13.     int ibase2;
  14.     Base2():ibase2(20) {}
  15.     virtual void f() { cout << "Base2::f()" << endl; }
  16.     virtual void g() { cout << "Base2::g()" << endl; }
  17.     virtual void h() { cout << "Base2::h()" << endl; }
  18. };
  19. class Base3
  20. {
  21.   public:
  22.     int ibase3;
  23.     Base3():ibase3(30) {}
  24.     virtual void f() { cout << "Base3::f()" << endl; }
  25.     virtual void g() { cout << "Base3::g()" << endl; }
  26.     virtual void h() { cout << "Base3::h()" << endl; }
  27. };
  28. class Derive : public Base1, public Base2, public Base3
  29. {
  30.   public:
  31.     int iderive;
  32.     Derive():iderive(100) {}
  33.     virtual void f() { cout << "Derive::f()" << endl; }
  34.     virtual void g1() { cout << "Derive::g1()" << endl; }
  35. };
  36. Derive d;
  37. Base1 *pb1 = &d;
  38. 00411C36  lea         eax,[ebp-0D8h] 
  39. 00411C3C  mov         dword ptr [ebp-0F0h],eax 
  40. Base2 *pb2 = &d;
  41. //原来指针赋值时编译器就做了手脚
  42. 00411C42  lea         eax,[ebp-0D8h]           ;取对象d的地址
  43. 00411C48  test        eax,eax                  ;判断地址是否为0
  44. 00411C4A  je          main+2EAh (411C5Ah)       
  45. 00411C4C  lea         ecx,[ebp-0D0h]           ;这里地址向后偏移了8 
  46. 00411C52  mov         dword ptr [ebp-5B4h],ecx 
  47. 00411C58  jmp         main+2F4h (411C64h) 
  48. 00411C5A  mov         dword ptr [ebp-5B4h],0 
  49. 00411C64  mov         edx,dword ptr [ebp-5B4h] 
  50. 00411C6A  mov         dword ptr [ebp-0FCh],edx ;偏移后的地址送往pb2
  51. Base3 *pb3 = &d;
  52. pb1->f();          // Derive::f(),实现了动态绑定
  53. //看下汇编代码,vs2005
  54. 00411C9E  mov         eax,dword ptr [ebp-0F0h]  eax:0x0012fe90(d的起始地址) 
  55. 00411CA4  mov         edx,dword ptr [eax] 
  56. 00411CA6  mov         esi,esp 
  57. 00411CA8  mov         ecx,dword ptr [ebp-0F0h] 
  58. 00411CAE  mov         eax,dword ptr [edx] 
  59. 00411CB0  call        eax
  60. pb2->f();          // Derive::f(),汇编代码跟pb1->f()差不多,那怎么定位到Derive中Base2的虚表呢,发现这里pb2的值已经不再指向d的首地址了,而是偏移了Base1对象的大小,这样就可以找到正确的虚指针了,看下上面的赋值语句,看看编译器是怎么做的
  61. 00411CB9  mov         eax,dword ptr [ebp-0FCh] eax:0x0012fe98(偏移了Base1的大小8)
  62. 00411CBF  mov         edx,dword ptr [eax] 
  63. 00411CC1  mov         esi,esp 
  64. 00411CC3  mov         ecx,dword ptr [ebp-0FCh] 
  65. 00411CC9  mov         eax,dword ptr [edx] 
  66. 00411CCB  call        eax  
  67. pb3->f();          // Derive::f()
  68. pb1->g();          // Base1::g()
  69. pb2->g();          // Base1::g()
  70. pb3->g();          // Base1::g()
还有一点不太明白,编译器怎么看到这句Base2 *pb2 = &d;就知道指针要向后偏移呢,编译器并不知道指向的是一个Derive类的对象,根据继承关系吗?不太明白,还请知道的指点一下!


原创粉丝点击