虚函数表问题

superbtl 2009-06-20 11:35:11
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);

Base b;

Fun pFun = NULL;

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

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

// Invoke the first virtual function

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

pFun();


为什么虚函数表地址跟类的变量b的地址一样?
取虚函数的时候 我们可以通过强行把&b转成int *,取得虚函数表的地址 这个应用的是什么原理啊?
(Fun)*((int*)*(int*)(&b)+1); // Base::g() 每次取虚函数 为什么还要用(int*)*虚函数地址?
问题比较多,麻烦解答下
...全文
61 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
andylovecandy 2009-06-20
  • 打赏
  • 举报
回复
(1)虚函数表地址跟类的变量b(对象)的地址不一样,但是虚函数表指针放在b的开始位置(不同编译器情况不同,这里是VC6的),
所以说虚函数表指针地址跟b地址一样。
虚函数表指针地址应该是(int*)(&b)
虚函数表地址应该是(int*)*(int*)(&b)
虚函数表 — 第一个函数地址应该是*((int*)*(int*)(&b))
由此可推:第二个虚函数地址应该是*((int*)*(int*)(&b)+1)

(2)因为虚函数表是4个字节对齐,int是4个字节(在VC中)
superbtl 2009-06-20
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 wjksd 的回复:]
(Fun)*((int*)*(int*)(&b)+1); // Base::g() 每次取虚函数 为什么还要用(int*)*虚函数地址?和数组的原理相似,画图好好想想
[/Quote]
说具体些,数组的首地址跟第一个元素的地址是一样的 这里的虚函数表的地址跟第一个虚函数的地址是不一样的
应该不一样 是不是说错了啊
nicky_zs 2009-06-20
  • 打赏
  • 举报
回复
这种问题回答起来很麻烦。。。 难得打那么多字

另外,你这个程序是编译器相关的,也就是说,C++标准并没有规定虚指针在对象中的位置,有些编译器把它放在最开始处,也会有编译器把它放在最末尾处。而虚表中的内容,更是编译器相关了。如果编译器想放一些额外的信息,那也未尝不可。不过,VC++是符合你的程序的。
superbtl 2009-06-20
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 wjksd 的回复:]
(Fun)*((int*)*(int*)(&b)+1); // Base::g() 每次取虚函数 为什么还要用(int*)*虚函数地址?和数组的原理相似,画图好好想想
[/Quote]
谢谢指引
superbtl 2009-06-20
  • 打赏
  • 举报
回复
三个问题 麻烦挨个回答下 期待高手
superbtl 2009-06-20
  • 打赏
  • 举报
回复
你们说的我都懂 可是具体原理是什么啊
  • 打赏
  • 举报
回复
pFun = (Fun)*((int*)*(int*)(&b));

*(int*)(&b)); //b的指针转为(int *)型,并去第一个元素,也就是虚表的地址值
(Fun)*((int*)*(int*)(&b)); //虚表地址转为(int *),再取第一个元素,就是第一个函数指针,转为(Fun)型

http://blog.csdn.net/hairetz/archive/2009/04/29/4137000.aspx
wjksd 2009-06-20
  • 打赏
  • 举报
回复
(Fun)*((int*)*(int*)(&b)+1); // Base::g() 每次取虚函数 为什么还要用(int*)*虚函数地址?和数组的原理相似,画图好好想想
only_delusion 2009-06-20
  • 打赏
  • 举报
回复
哎 顶下不会
wjksd 2009-06-20
  • 打赏
  • 举报
回复
C++ 对象的内存布局最先面的是虚表的地址,所以说最前面的是一个指针,就和数组是一个意思,第一个元素的地址和数组的地址相同。你想要访问一个指针指向的内容必然要解引用。
deerwin1986 2009-06-20
  • 打赏
  • 举报
回复
在VS及大部分编译器中,每个类中虚函数表的位置都在这个类的开头处,所以可以强制转换为指向一个整形的指针(因为是32位),然后再由这个虚函数表的起始位置步进到相应的虚函数地址...
不知道这样说是否明白.
具体看<<深度探索C++对象模型>>.......
wjksd 2009-06-20
  • 打赏
  • 举报
回复
虚函数表在最前面的位置。成员变量声明顺序依次放在后面。所以b的地址就是虚表的地址,虚表在b中只是一个int *指针指向的,这个指针指向的是具体的虚表
Walf_ghoul 2009-06-20
  • 打赏
  • 举报
回复
虚函数表中会记录下类中依次定义的虚函数的地址偏移量,整型的字节数。虚函数存放在静态区,通过地址调用。
superbtl 2009-06-20
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 sfeqbxeq 的回复:]
看不明白,学习
[/Quote]
谢谢帮我顶起
sfeqbxeq 2009-06-20
  • 打赏
  • 举报
回复
看不明白,学习
superbtl 2009-06-20
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 wjksd 的回复:]
考六级了,等回来再看看你有问题没啊。
[/Quote]
这个问题解决了我一些疑惑 谢谢了 分不多 所以给少了些 笑纳啊
superbtl 2009-06-20
  • 打赏
  • 举报
回复
[Quote=引用 23 楼 wjksd 的回复:]
看来你并没有了解c++对象的内存结构啊。我说的和数组的原理相似应该这么理解。
把对象b看做是一个数组,第一个元素(也就是b[0])是是一个指针,这个指针指向的是一个虚表,第二,三..个元素是成员变量.而在这个数组中的第一个元素b[0]这个指针指向的又是一个类似与数组的虚表,这个虚表的第一个元素,第二个元素..分别是虚函数的地址。我说的这个过程就解释了你所有的三个问题,画个图看看吧,我想把图放上来,但是不会弄。平…
[/Quote]
22楼已经解决了 我没太说清楚问题,你解决的也不是那个方向的
wjksd 2009-06-20
  • 打赏
  • 举报
回复
考六级了,等回来再看看你有问题没啊。
wjksd 2009-06-20
  • 打赏
  • 举报
回复
看来你并没有了解c++对象的内存结构啊。我说的和数组的原理相似应该这么理解。
把对象b看做是一个数组,第一个元素(也就是b[0])是是一个指针,这个指针指向的是一个虚表,第二,三..个元素是成员变量.而在这个数组中的第一个元素b[0]这个指针指向的又是一个类似与数组的虚表,这个虚表的第一个元素,第二个元素..分别是虚函数的地址。我说的这个过程就解释了你所有的三个问题,画个图看看吧,我想把图放上来,但是不会弄。平时总潜水,不怎么回答问题。
nicky_zs 2009-06-20
  • 打赏
  • 举报
回复
第三个问题:
1、(int *)(&b)是你得到的虚指针的地址,虚指针指向虚表;
2、*(int *)(&b)是对虚指针的解引用,从而得到了虚表的地址,但现在它还是以int整数的形式保存着的;
3、(int *)*(int *)(&b)是将得到的表示虚表地址的整数转换成一个指针,如果不转换成指针,就无法再解引用;
4、*((int *)*(int *)(&b))是对虚表的首地址进行解引用,从而得到函数地址。

为了方便你理解,我把你的代码修改了一下,更准确一点:

#include <iostream>

using namespace std;

class Base
{
public:
Base(int a, int b, int c) : i(a), j(b), k(c) {}

virtual void f() { cout << "第一个函数, i = " << i << endl; }
virtual void g() { cout << "第二个函数, j = " << j << endl; }
virtual void h() { cout << "第三个函数, k = " << k << endl; }

private:
int i, j, k;
};

typedef void (__fastcall *Fun)(Base const *);

int main(int argc, char *argv[])
{
Base b(33, 66, 99);
Fun pFun = NULL;

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

cout << "虚函数表 — 第一个函数地址:" << (int *)*(int *)(&b) << endl;
pFun = (Fun)*((int *)*(int *)(&b));
pFun(&b);

cout << "虚函数表 — 第二个函数地址:" << (int *)*(int *)(&b) + 1 << endl;
pFun = (Fun)*((int *)*(int *)(&b) + 1);
pFun(&b);

cout << "虚函数表 — 第三个函数地址:" << (int *)*(int *)(&b) + 2 << endl;
pFun = (Fun)*((int *)*(int *)(&b) + 2);
pFun(&b);
}
加载更多回复(6)

64,648

社区成员

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

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