请教一个关于虚函数表的表项的内容的问题

benbshmily 2010-03-15 11:02:01
class Base { 

public:

virtual void f() { cout << "Base::f" << endl; }

virtual void g() { cout << "Base::g" << endl; }

virtual void h() { cout << "Base::h" << endl; }

};
typedef void(*Fun)(void);

int main()
{


Base b;

Fun pFun = NULL;

cout << "虚函数表地址:" << (int*)(&b) << endl;

cout << (int*)*(int*)(&b) << endl; //(1)
cout << (int*)*((int*)*(int*)(&b)) << endl; //(2)

pFun = (Fun)*((int*)*(int*)(&b)); //(3)

pFun();

system("pause");
return 0;

}

程序中(1)处和(2)处打印出的值分别代表什么意思? 那一处是表示函数Based::f的入口地址。


上面的问题是在坛子上看到一个贴子想到的。我以前一直是以为类的虚函数表的表项的内容就是对应的虚函数的入口地址。那为什么在调用这个函数的时候还得对这个地址取一次* 然后再调用呢。见程序的标注的(3).而不是pFun = (Fun)*(int*)(&b);pFun();这样调用呢。

我知道 对于普通的全局函数,比如
void fun() { cout << "func f" << endl; }
fun的值就是该函数的入口地址。就可以用
typedef void(*Fun)(void);

Fun pf = fun; pf()。调用。

请问一下,我哪个地方错了。谢谢赐教。
...全文
156 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
benbshmily 2010-03-15
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 hairetz 的回复:]

cout << (int*)*(int*)(&b) << endl; //(1)
cout << (int*)*((int*)*(int*)(&b)) << endl; //(2)

pFun = (Fun)*((int*)*(int*)(&b)); //(3)


1的内容是b对象第一个32位的值,也就是虚表的地址。 可以理解吧
2明显是虚表的……
[/Quote]

看了你的文章,让我明白了哈。谢谢。确实(int*)*(int*)(&b)这个才是虚表的地址。然而 虚表的表项内容也确实是虚函数的入口地址了。谢谢。
benbshmily 2010-03-15
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 hairetz 的回复:]

cout << (int*)*(int*)(&b) << endl; //(1)
cout << (int*)*((int*)*(int*)(&b)) << endl; //(2)

pFun = (Fun)*((int*)*(int*)(&b)); //(3)


1的内容是b对象第一个32位的值,也就是虚表的地址。 可以理解吧
2明显是虚表的……
[/Quote]

*(int*)(&b)这个不是虚表的地址吧,(int*)(&b)这个才是虚函数表的地址吧。
昏,我完全糊涂了?

1的内容是b对象第一个32位的值,也就是虚表的地址

na2650945 2010-03-15
  • 打赏
  • 举报
回复
等高手解答。
  • 打赏
  • 举报
回复
cout << (int*)*(int*)(&b) << endl; //(1)
cout << (int*)*((int*)*(int*)(&b)) << endl; //(2)

pFun = (Fun)*((int*)*(int*)(&b)); //(3)


1的内容是b对象第一个32位的值,也就是虚表的地址。 可以理解吧
2明显是虚表的地址指向的第一个内容,也就是第一个虚函数指针。

pFun = (Fun)*((int*)*(int*)(&b)); //(3)
所以3就是第一个虚函数。

http://blog.csdn.net/hairetz/archive/2009/04/29/4137000.aspx

我转过,看我的红字。
benbshmily 2010-03-15
  • 打赏
  • 举报
回复
先吃饭,回来有了答复就结贴。谢谢大家啦。麻烦大家帮我澄清一下这个问题呀,不然我这一天都过不好的。~_~
benbshmily 2010-03-15
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 freezezdj 的回复:]

pf()或者(*pf)()
c++支持两种调用方式。
你不要想是为什么了。
可能只是为了方便使用。
[/Quote]
那是不是规范的调用应该是(*pf)()这种方式呢?c++为了支持书写方便,pf()这种调用也是可以的。如果是这样那就能说通一点问题。
冻结 2010-03-15
  • 打赏
  • 举报
回复
pf()或者(*pf)()
c++支持两种调用方式。
你不要想是为什么了。
可能只是为了方便使用。
冻结 2010-03-15
  • 打赏
  • 举报
回复
楼主这篇文章我也看到过。
benbshmily 2010-03-15
  • 打赏
  • 举报
回复
从上面的程序来看,那是不是虚函数表表项的内容是一个指向对应虚函数的一个函数指针呢?要取得这个虚函数的入口地址 就得对这个表项的内容取一次*操作。要是是这样的话。
那对于普通的全局函数又怎么说呢?

对于普通的全局函数,比如
void fun() { cout << "func f" << endl; }
fun的值就是该函数的入口地址。就可以用
typedef void(*Fun)(void);

Fun pf = fun; pf()或者(*pf)()。调用都是可以的。意思就是说pf的值和*pf的值是一样的喽。越想越不明白
_JeffreyWu 2010-03-15
  • 打赏
  • 举报
回复
MARK
taodm 2010-03-15
  • 打赏
  • 举报
回复
去看《深度探索C++东西模型》吧,网文就少看吧。
机智的呆呆 2010-03-15
  • 打赏
  • 举报
回复
可能上面回复比较乱~~lz再结合这篇文章理解一下:
http://blog.csdn.net/Demon__Hunter/archive/2009/04/30/4140603.aspx
机智的呆呆 2010-03-15
  • 打赏
  • 举报
回复
从基础讲起:比如 int a=0;那么编译器会在内存中分配sizeof(int)的字节,比如说sizeof(int)=4,
那么就分配四个字节的内存 假设这四个字节内存是地址值0x002faa00~0x002faa03的这4个字节的内存。
那么以后对a变量的操作,编译器会映射到对这4个字节里的数据操作,比如a=1;那么编译器会把1按照int类型
数据的组织方式写入0x002faa00~0x002faa03的这4个字节的内存。
int *p=&a;同样指针变量p也对应一段存储空间,假设指针变量占4个字节的内存,对应的地址值是0x002fbb00~0x002fbb03,这4个字节里的内存存储的是变量a的地址,可能是按照如下方式存储的:地址值为0x002fbb00内存里存储00,0x002fbb01存储aa,0x002fbb02存储2f,0x002fbb03存储00,p的值是0x002faa00。而*p=1;实质从p值对应的内存地址开始,往下涵盖sizeof(p指向的类型)大小的内存,将这段内存按照p指向的类型的方式写入1,即把0x002faa00~0x002faa03这段内存里的数据按照int类型数据的组织方式写入1,int b=*p;则是从p值对应的内存地址开始,往下涵盖sizeof(p指向的类型)大小的内存,将这段内存按照p指向的类型的方式取出赋值给变量b。

如果上述问题清楚,那么来讲下虚表:
如果类中有虚拟成员函数,那么编译器会在类的对象内安插虚表指针,绝大部分的编译器在对象起始的4个字节安插一个虚表指针,也就是前4个字节可以认为是某一个虚表指针变量的存储空间,假设是int* vtbl;那么指针变量vtbl对应4个的四个字节的存储空间即是对象起始的4个字节的内存,&b相当于&vtbl,也就是假想的虚表指针变量vtbl的地址,我们知道这个虚表指针指向一个虚表,也就是说这个虚表指针的值是虚表的地址,那么我们怎么获得虚表指针的值那?*(int*)&b就是。(int*)&b对应的值被强制转换成一个int*指针变量的值,在*解引用时,他会把从&b开始的内存里的数据按照上面所说解析方式的解读出来,*(int*)&b值即是我们假想的虚表指针的值,到现在为止我们获取了虚表的存储地址,而虚表中实际存储的是函数指针的值,可以知道指针的值win32占4个字节,假设*(int*)&b值是0x07000000,那么说明虚表的存储地址是0x07000000,可以知道0x07000000~0x07000003是第一个函数指针的值,0x07000004~0x07000007是第二个,依次类推...,那么如何通过0x07000000获取第一个函数指针的值?*((int*)*(int*)(&b)),即*((int*)0x07000000)相当于把0x07000000~0x07000003值取出,这才是第一个函数指针的值。
we_sky2008 2010-03-15
  • 打赏
  • 举报
回复
cout << "虚函数表地址:" << (int*)(&b) << endl;
这句不对吧?(int*)(&b)只是对象的地址吧?
当编译器把VPTR安插在对象的最前面时,(int*)(&b)可以看做是VPTR的地址
我觉得(int*)*(int*)(&b)才是虚函数表地址

64,692

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧