关于VS2005Debug时看到的虚表

always_learn 2013-03-19 10:17:42
代码如下:

class A
{
public:
virtual void vFun();
};

class B:public A
{
public:
virtual void vFun2();
};

class C:public B
{
void vFun2();
};

void A::vFun()
{
cout << "I'm A::vFun()" << endl;
}

void B::vFun2()
{
cout << "I'm B::vFun2()" << endl;
}

void C::vFun2()
{
cout << "I'm C::vFun2()" << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
B *b = new C();
b->vFun2();//断点处
system("pause");
return 0;
}

此段代码输出结果是I'm C::vFun2()应该没什么异议。问题出在当程序运行到断点处,看到的__vfptr的疑问,见下图:

此处可以看到虚表中只有一个函数的入口地址,为什么B::vFun2(void)不在里面呢?
...全文
261 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2013-03-19
  • 打赏
  • 举报
回复
《深度探索C++对象模型》
always_learn 2013-03-19
  • 打赏
  • 举报
回复
结论: 先构造A: __vfptr: A::vFun() # 再构造B: __vfptr: A::vFun() B::vFun2() 最后构造C(C重写B的vFun2): __vfptr: A::vFun() C::vFun2() * VS2005打印依据#处看到的函数,但实际的内存分布还是*处看到的结果。 求权威人士认证结论是否合理。
always_learn 2013-03-19
  • 打赏
  • 举报
回复
现在目测得出的结论是,VS2005在打印对象的虚表时,只打印最上层基类申明的虚函数。。。
_sunshine 2013-03-19
  • 打赏
  • 举报
回复
引用 14 楼 sccdyy226 的回复:
原理上C::vFun2(void)肯定存在于c对象的虚表中,这个我是明白的,否则就不会输出I'm C::vFun2()的结果,主要是想知道为啥VS2005不将他打印出来。。。
上面测试代码的解释: (int*)(&c)得到的是一个指向第一张虚表地址的指针,该虚表中存放的是虚函数的函数指针,每项占4字节。对该地址取内容:*(int*)(&c) 可以得到第一张虚表的首地址,但应该将该值转换成int*,使得每次指针+1操作都内存向前移动4字节,即虚表中一个元素的长度:(int*)*(int*)(&c)。因为虚表中元素占4字节,现在把这个指针向前移动一个元素: (int*)*(int*)(&c)+1,得到表中第二项的地址,对该项取内容,得到函数的入口地址,即C::vFun2()的入口地址:*((int*)*(int*)(&c)+1). 得到这个值以后还不能直接调用,应该将该函数入口点转换成相应的函数原型(FUN)(*((int*)*(int*)(&c)+2)) 这个就跟编译器的设计相关了,这个我也不知道
always_learn 2013-03-19
  • 打赏
  • 举报
回复
原理上C::vFun2(void)肯定存在于c对象的虚表中,这个我是明白的,否则就不会输出I'm C::vFun2()的结果,主要是想知道为啥VS2005不将他打印出来。。。
_sunshine 2013-03-19
  • 打赏
  • 举报
回复
引用 12 楼 sccdyy226 的回复:
现在的情况是从A到B新增一个虚函数,从B到C没有新增。A->B(add)->C, 而那篇文章中的情况应该是A->B>C(add), 我觉得应该不一样的吧。。。。

int main()
{
    C c;
    typedef void (*FUN)();   //函数指针
    FUN run_crazily = (FUN)(*((int*)*(int*)(&c)+1)); 
    run_crazily();
    system("pause");
    return 0;
}
楼主试试上面的测试代码就知道了
always_learn 2013-03-19
  • 打赏
  • 举报
回复
现在的情况是从A到B新增一个虚函数,从B到C没有新增。A->B(add)->C, 而那篇文章中的情况应该是A->B>C(add), 我觉得应该不一样的吧。。。。
_sunshine 2013-03-19
  • 打赏
  • 举报
回复
只是多了一层继承,编译器的虚表始终与最顶层(基类在上)的父类保持一致
_sunshine 2013-03-19
  • 打赏
  • 举报
回复
不是一个道理吗?都是出现新的函数啊,你可以动手测试下
always_learn 2013-03-19
  • 打赏
  • 举报
回复
那篇文章谈到的东西和现在还是有点不一样,你仔细看下B类在A类的基础上新增了一个虚函数,而那篇文章中整个继承体系中没有在中间出现新的虚函数。。。
_sunshine 2013-03-19
  • 打赏
  • 举报
回复
那篇文章不是有提到:在Clion中定义了一个Clion自己的虚函数RunCrazily,可是从调试窗口中并没有看到RunCrazily。那么这张表到底存放在哪里呢?通过网上查阅资料,得知这张表其实是存放在第一张虚表中的。
always_learn 2013-03-19
  • 打赏
  • 举报
回复
引用 6 楼 dy106 的回复:
引用 3 楼 sccdyy226 的回复:再者如果B::vFun2(void)不在C对象的虚表中,程序输出就应该是I'm B::vFun2(),难道这个是调试器的bug? 楼主可以参考下:http://www.cnblogs.com/chenkunyun/archive/2012/03/15/2397823.html
谢谢,看了这篇文章还是有点疑问我先理解下,顺便纠正下我想问的内容,有个拼写错误,为什么B::vFun2(void)不在里面呢?应该改为: 为什么C::vFun2(void)不在里面呢?
_sunshine 2013-03-19
  • 打赏
  • 举报
回复
引用 3 楼 sccdyy226 的回复:
再者如果B::vFun2(void)不在C对象的虚表中,程序输出就应该是I'm B::vFun2(),难道这个是调试器的bug?
楼主可以参考下:http://www.cnblogs.com/chenkunyun/archive/2012/03/15/2397823.html
漫步者、 2013-03-19
  • 打赏
  • 举报
回复
引用 楼主 sccdyy226 的回复:
代码如下: C/C++ code ? 12345678910111213141516171819202122232425262728293031323334353637383940 class A { public: virtual void vFun(); }; class B:public A { public: virtual void v……
方法被重写,只有一份拷贝。
_sunshine 2013-03-19
  • 打赏
  • 举报
回复
引用 2 楼 sccdyy226 的回复:
但是B是C的父类呀?
B还继承与A,它的虚表必须和A一致,假如像下面这样写:

int main()
{
    A *b = new B();
    b->vFun2();//会报错的
    system("pause");
    return 0;
}
always_learn 2013-03-19
  • 打赏
  • 举报
回复
再者如果B::vFun2(void)不在C对象的虚表中,程序输出就应该是I'm B::vFun2(),难道这个是调试器的bug?
always_learn 2013-03-19
  • 打赏
  • 举报
回复
但是B是C的父类呀?
_sunshine 2013-03-19
  • 打赏
  • 举报
回复
因为vFun2(void)只存在于B中,A中并没有这个函数,虚表只有父类中的虚函数的入口地址
using System; using System.Runtime.InteropServices; using System.Reflection; using System.Windows.Forms; namespace KeyboardHook { public enum KeyboardEvents { KeyDown = 0x0100, KeyUp = 0x0101, SystemKeyDown = 0x0104, SystemKeyUp = 0x0105 } [StructLayout(LayoutKind.Sequential)] public struct KeyboardHookStruct { public int vkCode; //表示一个在1到254间的虚似键盘码 public int scanCode; //表示硬件扫描码 public int flags; public int time; public int dwExtraInfo; } public delegate void KeyboardEventHandler(KeyboardEvents keyEvent, System.Windows.Forms.Keys key); public class Hook { public event KeyboardEventHandler KeyboardEvent; public enum HookType { WH_JOURNALRECORD = 0, WH_JOURNALPLAYBACK = 1, WH_KEYBOARD = 2, WH_GETMESSAGE = 3, WH_CALLWNDPROC = 4, WH_CBT = 5, WH_SYSMSGFILTER = 6, WH_MOUSE = 7, WH_HARDWARE = 8, WH_DEBUG = 9, WH_SHELL = 10, WH_FOREGROUNDIDLE = 11, WH_CALLWNDPROCRET = 12, WH_KEYBOARD_LL = 13, WH_MOUSE_LL = 14, WH_MSGFILTER = -1, } public delegate IntPtr HookProc(int code, int wParam, IntPtr lParam); [DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc hook, IntPtr instance, int threadID); [DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern IntPtr CallNextHookEx(IntPtr hookHandle, int code, int wParam, IntPtr lParam); [DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern bool UnhookWindowsHookEx(IntPtr hookHandle); private IntPtr instance; private IntPtr hookHandle; private int threadID; private HookProc hookProcEx; public Hook()

64,637

社区成员

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

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