随笔 - 13  文章 - 36  trackbacks - 0
<2009年3月>
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

常用链接

留言簿(2)

随笔档案

友情链接

搜索

  •  

最新评论

阅读排行榜

评论排行榜

 转载自:http://blog.csdn.net/akirya/archive/2009/01/18/3825040.aspx


很多人对成员函数指针有无解,以为成员函数指针同普通的函数指针区别不大,多了一个参数.
然而实际上却不是.多了个参数,是不假,但他确实不是指针,虽说名字中有指针两个字,但实际上却不是指针.
先看看最简单的使用

class test
{
public:
 void func(){printf("call test::func\n");};
};

int main()
{
 void(test::*p)() = &test::func;
 test x;
 (x.*p)();
}

这里的用法是最常见的,跟普通的函数指针使用的地方也差不多。一般见到的地方也差不多都这么用。
但成员函数指针还有更好用的地方,看下面这个例子
class base
{
public:
 virtual void func(){printf("call base::func\n");};
};
class test:public base
{
public:
 void func(){printf("call test::func\n");};
};

int main()
{
 void(base::*p)() = &base::func;
 test x;
 (x.*p)();//调用的是test::func;
 base y;
 (y.*p)();//调用的是base::func;
}

从这个例子看,成员函数指针也可以使用多态。看到这里是不是觉得成员函数指针跟之前想的不一样?

紧接着上面的例子,只更改main函数的内容

int main()
{
 void(base::*p)() = &base::func;
 printf("sizeof( void(base::*p)()) %d\n" , sizeof(p) );
};


这里的输出结果能想到么?(我的系统是32位的XP)
VC9 下是 4
GCC4.2.1 是 8
CodeGear C++ 6.10 的结果是12
到这里是不是会想到如何得到成员函数的地址呢?
我想到最简单的办法就是输出map文件,这样就直接找到对应的函数的地址。
那运行时候怎么得到呢?
我的答案是没有,
如果是虚函数则可以通过搜索虚表来得到,不过这个方法太不通用,也很难实际应用。算是一种理论上能,但不能实际应用的方法。
非虚函数的话,还不知道有什么好的方法能够得到。

发表于 @ 2009年01月18日 10:19:00|评论(loading... )|收藏

新一篇: 如何写易于使用的代码 (一个宏引起的问题) | 旧一篇: 注册COM到单独用户

评论

#taodm 发表于2009-01-19 15:12:59  IP: 10.63.110.*
C++语言当初设计的时候就认为你不需要得到它的真正地址。
#taodm 发表于2009-01-20 15:09:58  IP: 10.63.110.*
C++在设计的时候就认为不(应该)需要获得成员函数的真实地址。
解决方案对你来说只是举手之劳。
#akirya 发表于2009-01-20 18:43:34  IP: 58.30.180.*
呵呵~taodm 见笑了:D
最初这个问题就是因为你我才去弄明白的
#zenny_chen 发表于2009-01-20 18:58:09  IP: 218.242.128.*
akirya大侠似乎没有解释成员函数指针为何不是指针,难道它属于别的什么东西?不过本人倒是觉得它应该算是指针吧。它具有指针的基本特性啊,呵呵。

另外,关于通过成员函数指针来获得成员函数地址的方法确实比较困难。它的困难点在于必须绕过C++编译器的类型检查。像VC对成员函数指针类型检查的很严,即使是void*类型都不能转,也无法通过reinterpret_cast、dynamic_cast之类的来转。
因此我这里将采取暴力手段来获取:

#include <iostream>
using namespace std;


class Test
{
public:

void Hello(void)
{
cout << "Hello, world!" << endl;
}

void Hello(int i)
{
cout << "The answer is: " << i << endl;
}
};


template <typename T>
inline unsigned GetMemberFuncAddress(T p)
{
unsigned result;

__asm
{
mov eax, dword ptr [p]
mov dword ptr [result], eax
}

return result;
}


int main(void)
{
void (Test::* p)(void) = &Test::Hello;
void (Test::* q)(int) = &Test::Hello;

(Test().*p)();
(Test().*q)(100);

cout << "The address is: " << p << endl;
cout << "The address is: " << q << endl;

cout << "The address is: 0x" << hex << GetMemberFuncAddress(p) << endl;
cout << "The address is: 0x" << hex << GetMemberFuncAddress(q) << endl;

return 0;
}
#akirya 发表于2009-01-20 19:28:46  IP: 58.30.180.*
很遗憾,VC不代表C++

CodeGear C++ 6.10 下 sizeof(成员指针大小)结果是12
这个时候你如何输出呢?12字节大小的指针?
#healer_kx 发表于2009-01-20 19:49:41  IP: 125.33.137.*
你们两个真无聊,我都开始研究JS了
#zenny_chen 发表于2009-01-20 20:22:15  IP: 218.242.128.*
哈哈,healer_kx兄挺与时俱进的嘛。

呵呵,虽然没用过CodeGear,但不管是基于怎样的处理器都可以获得指针值吧。只要匹配好地址类型就行。我刚才就是忘了说前提了。实际上如果要考虑到兼容性的话就将地址类型大小指明一下就行啊。
#ifdef ADDRESS_32BIT
typedef unsigned TYPE_ADDRESS;
#elif defined(ADDRESS_64BIT)
typedef unsigned long long TYPE_ADDRESS;
#elif defined(ADDRESS_96BIT)
typedef struct
{
unsigned low4;
unsigned mid4;
unsigned high4;
}TYPE_ADDRESS;
#endif

虽然我不知道CodeGear的汇编形式,不过我这里就再借助一下MASM了,呵呵:

template <typename T>
inline TYPE_ADDRESS GetMemberFuncAddress(T p)
{
TYPE_ADDRESS result;

#ifdef ADDRESS_32BIT
#define WORD_SIZE dword
#define MOV_FORM mov
#define REG_OPT eax
#elif defined(ADDRESS_64BIT)
#define WORD_SIZE mmword
#define MOV_FORM movq
#define REG_OPT mm0
#elif defined(ADDRESS_96BIT)
#define WORD_SIZE xmmword
#define MOV_FORM movdqu
#define REG_OPT xmm0
#endif

__asm
{
MOV_FORM REG_OPT, WORD_SIZE ptr [p]
MOV_FORM WORD_SIZE ptr [result], REG_OPT
}

return result;
}
#akirya 发表于2009-01-20 22:16:25  IP: 58.30.180.*
zenny_chen很专业

那假如是指针的话,那多态例子怎么解释,对象不同调用的函数也就不用? 但成员函数函数指针的值并没有改变。
#zenny_chen 发表于2009-01-20 23:33:34  IP: 116.234.182.*
呵呵,如果是虚函数的话这样整肯定不行了。而且每种编译器对于对象模型模型可能建的不一样,而且虚函数表的建立机制也可能大相径庭,所以访问虚函数指针的值的方式也会不同。
我上面仅仅是针对你最后一句话的回答,呵呵。
#akirya 发表于2009-01-20 23:54:32  IP: 58.30.180.*
的确不是指针,在CodeGear C++ 6.10(也就是BCB的后续版本)存的是一个偏移量.
sizeof( 成员函数指针)大小是12也就说明了指针这种假设是不成立的,具体存的是取决与编译器的实现。

标准也没有对成员函数指针里面存的是啥东西做规定了,我写这个目的就是说明一下,成员函数指针不是真正的指针,里面保存的不一定是地址。
#taodm 发表于2009-01-21 09:45:21  IP: 10.63.110.*
又有汇编高手冒出来了。接着又要探讨获取构造函数、析构函数真实地址的暴力手法了。。。。。。
#zenny_chen 发表于2009-01-21 13:31:21  IP: 218.242.128.*
哈哈,taodm真是搞笑啊。

嗯……如果是虚成员函数的话其地址是虚的嘛。所以拿到的函数地址可以认为是“虚指针”,呵呵。而且编译器可以在编译时断定对象所调用的函数是否为虚函数,对象是否具有虚函数表指针。所以这些在编译时就搞定的东西即使用暴力手段也难以获取,呵呵。
我想说的是“虚指针”算是一种特殊的指针罢。
posted on 2009-03-18 11:06 Alex-Lee 阅读(1051) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理