Chinaunix首页 | 论坛 | 博客
  • 博客访问: 178013
  • 博文数量: 43
  • 博客积分: 827
  • 博客等级: 准尉
  • 技术积分: 487
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-26 19:19
文章分类

全部博文(43)

文章存档

2015年(1)

2014年(1)

2013年(5)

2012年(36)

我的朋友

分类: C/C++

2012-04-07 22:02:15

本文主要针对成员函数指针,描述了相关成员函数指针的一些性质。

问题的引入

前些时候,对于一个功能涉及到了成员函数的使用,具体过程是,在一个模板里面,有一个内嵌Wrapper结构,这个Wrapper包含一个对象指针和一个成员函数指针,大致代码如下:

点击(此处)折叠或打开

  1. // Template .h file

  2. template <typename T>
  3. class TClass
  4. {
  5. ...
  6.     struct Wrapper
  7.     {
  8.         typedef ret(T::* Handler)(args);
  9.         T* pSubject;
  10.         Handler pHandler;
  11.     };
  12. ...
  13.     void SomeFunc(T* pS, Wrapper::Handler pH)
  14.     {
  15.         m_wrapper.pSubject = pS;
  16.         m_wrapper.pHandler = pH;// Where problem occurs
  17.     }
  18. ...
  19.     Wrapper m_wrapper;
  20. };
而在使用上面类的时候,大致如下

点击(此处)折叠或打开

  1. // In some .h file

  2. class SubjectClass; // In some .cpp file who include this .h file, compiler still can't know the definition of subject class.

  3. typedef TClass<SubjectClass> SubjectTClass;
  4.  
  5. ...
当一切都写好了,运行程序的时候,悲剧来了,m_wrapper.pHandler = pHpH是一个有效的成员函数指针,但是m_wrapper.pHandler就是赋不上值。
好,转到反汇编看一看
注:下面的汇编码仅仅是为模拟问题,在VS 2008上生成

点击(此处)折叠或打开

C++ Code :
  1. class A;
  2. typedef void(A::* AMemFunc)();
  3. AMemFunc memFunc;
  4. memFunc = 0;   // Here "memFunc = 0" simulates "m_wrapper.pHandler = pH"
Assemble Code :
  1. 012613BE mov dword ptr [ebp-0ECh],0
  2. 012613C8 mov dword ptr [ebp-0E8h],0
  3. 012613D2 mov dword ptr [ebp-0E4h],0
  4. 012613DC mov dword ptr [ebp-0E0h],0FFFFFFFFh
  5. 012613E6 mov eax,dword ptr [ebp-0ECh]
  6. 012613EC mov dword ptr [memFunc],eax         // First byte of memFunc
  7. 012613EF mov ecx,dword ptr [ebp-0E8h]
  8. 012613F5 mov dword ptr [ebp-10h],ecx         // Second byte of memFunc
  9. 012613F8 mov edx,dword ptr [ebp-0E4h]
  10. 012613FE mov dword ptr [ebp-0Ch],edx         // Third byte of memFunc
  11. 01261401 mov eax,dword ptr [ebp-0E0h]
  12. 01261407 mov dword ptr [ebp-8],eax           // Forth byte of memFunc
m_wrapper.pHandler = pH 这一行代码生成的汇编码就如上面1~12的汇编码一样,这是啥玩意???
究其原因就在于在一个.cpp编译单元里面,对于类的成员函数指针,如果编译器看不到这个类的定义,那么编译器生成的成员函数指针的代码就如上面,no warning,no error。

成员函数指针解析

对于成员函数指针,不要期望他的size和普通函数指针的size是一致的,这区别于这个成员函数指针所属类是怎样的类。
1:单继承情况
对于单继承,(如果)所有的成员函数期望的this指针都是一致的,没有对this指针施加offset,(那么)这个时候这种类的成员函数指针和普通的函数指针大小是一样的,4 bytes。
low ---
      4bytes  funcPtr
      ---
这种情况下,成员函数的调用汇编码为:
lea   ecx,  [obj]                  // this pointer
call  dword ptr[memFunc]  // invoke member function

注意:对于一般的单继承情况,父类和子类的this指针是一样的,但是对于这种情况
class A{...};// A doesn't have virtual function
class B : public A{...}; // B has virtual function
A和B的this指针是不一样的,A的成员函数期望的this = B的成员函数中期望的this+4bytes,因为B到A要跳过vfptr指针。
所以这种情况涉及到了this指针的偏移,因此B的成员函数指针为8bytes。4bytes(FuncPtr)+4bytes(Offset)
这种情况和2情况是类似的

2:多继承情况
对于多继承,对于不同基类的成员函数(多态/非多态成员函数),他们期望的this指针和子类本身的this指针一般是需要一个offset的。那么这个时候,对于多继承类的成员函数指针不仅要记录实际成员函数的地址,还要记录一个对于该类this的一个offset。则这个时候为8 bytes。

low ---
      4bytes    funcPtr
      4bytes    offset
      ---
这个时候成员函数的调用汇编码为:
lea ecx,  [obj] // this of obj
add ecx, dword ptr[memFunc+4]        // this += offset  
call dword ptr[memFunc]                   // invoke member function

3:虚继承类情况
对于虚继承,他涉及到一个vbtable(virtual base table),对于虚继承的类,他的内存布局在本类的内存布局上需要到vbtable上去找到相对于vbptr(virtual base table pointer)所在地址的偏移,才能得到这个虚继承类的地址。
对于虚继承的内存模型例子
所以这个时候的成员函数指针大小为12bytes,他的内存模型为
low ----
      4bytes   funcPtr
      4bytes   offset
      4bytes   offset in vbtable
      ----
而成员函数调用时候的伪码为:
vbtbl_off    = offset in vbtable                                        // ’offset in vbtable‘ is the highest 4bytes in memFunc
vb_this       = addr_vbptr+*(dword*)(vbtable+vbtbl_off)  // ‘this’ for virtual base class
this            = vb_this + offset                                        
invoke member function

所以,对于成员函数指针,要区别于一般的函数指针。
对于这三种情况,这里只是大致的一个说明,更细致的情况,可写一个小程序看看他的反汇编码。

总结:
因为编译器对于成员函数指针的处理,在编译器处理非定义类的时候,具有这样的特殊的行为,而这种行为又不易察觉,所以个人觉得在使用成员函数的时候,自己有一个约定,约定总在定义类之后才使用该类的成员函数指针,而不要让编译器去决定。

对于C++在Multiple Inheritance和Virtual Inheritance的一些技术细节可参阅
Bjarne Stroustrup, "Multiple Inheritance for C++" ,
Jan Gray, "C++: Under the Hood"

阅读(793) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~