谁帮我讲解一下虚函数表指针

james_hw 2010-07-14 02:17:21
我设计两个类,一个基类和它的派生类,两个类都只有一个虚函数。在百度上查到“类的虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址”。因此我想,如果我将派生类对象覆盖了基类对象,是否会把基类的虚函数表指针更改呢?

测试的代码如下:

int *pa;
int *pb;

class A
{
public:
virtual print()
{
pa=(int *)this;
cout<<"A::print:"<<pa<<" "<<*pa<<" "<<*((int *)(*pa))<<endl;
}
};

class B:public A
{

public:
virtual print()
{
pb=(int *)this;
cout<<"B::print:"<<pb<<" "<<*pb<<" "<<*((int *)(*pb))<<endl;
}
};

int main()
{
B b;
b.print();
A a;
a.print();
memcpy(&a,&b,sizeof(a));
a.print();
return 0;
}



有兴趣的可以运行一下。
输出显示a的this指针保存的内容的确发生了变化,但是其仍能寻找的a的print函数。这让我对虚函数表指针产生了疑惑,这个指针明明指向的是b的虚函数表,为啥没有调用b的print函数呢?编译器是如何做到这一点的?

...全文
136 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
james_hw 2010-07-14
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 we_sky2008 的回复:]
只有指针或引用调用虚函数时,才会通过VPTR和Vtable来调用虚函数,
而通过对象调用虚函数,则是相当于静态调用该类的虚函数,因为调用者的类型都确定了,自然不通过虚表也能确定调用哪个函数了
当然,通过该对象的VPTR也可以调用正确的虚函数,但是却是不必要的
如:

C/C++ code

int main()
{
B b;
b.print();//静态调用,相……
[/Quote]

呵呵,这应该是我想要的答案,多谢!
we_sky2008 2010-07-14
  • 打赏
  • 举报
回复
只有指针或引用调用虚函数时,才会通过VPTR和Vtable来调用虚函数,
而通过对象调用虚函数,则是相当于静态调用该类的虚函数,因为调用者的类型都确定了,自然不通过虚表也能确定调用哪个函数了
当然,通过该对象的VPTR也可以调用正确的虚函数,但是却是不必要的
如:

int main()
{
B b;
b.print();//静态调用,相当于b.B::print();
A a;
a.print();//静态调用,相当于a.A::print();
memcpy(&a,&b,sizeof(a));//因VPTR一般是放在对象的头部,所以此处的a的VPTR指向B的虚表
a.print();//静态调用,相当于a.A::print();
b.print();//静态调用,相当于b.B::print();
A *p=&a;
p->print();//通过虚表调用,因a的vptr指向B的vtable,所以此处调用p->B::print();
return 0;
}

ztenv 版主 2010-07-14
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 selooloo 的回复:]
因为对象调用是不访问虚函数表的吧,指针调用才能体现动态调用
[/Quote]

虚函数是放到虚函数表的,如果对象调用不访问虚函数表,那函数地址从哪里来?难不成一个虚函数的地址存存储两次?
selooloo 2010-07-14
  • 打赏
  • 举报
回复
因为对象调用是不访问虚函数表的吧,指针调用才能体现动态调用
ztenv 版主 2010-07-14
  • 打赏
  • 举报
回复
应该不会,但为什么,我不清楚;
james_hw 2010-07-14
  • 打赏
  • 举报
回复
增加点信息



int main()
{
B b;
b.print();
A a;
a.print();
memcpy(&a,&b,sizeof(a));
a.print();
b.print();
A *p=&a;
p->print();
return 0;
}



p->print();找到的是B类空间的打印函数。感觉正像我想的,虚函数表指针发生了改变。但是a.print莫非不去检测虚函数表?
ztenv 版主 2010-07-14
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 jamesf1982 的回复:]
引用 1 楼 lianshaohua 的回复:
因为多态的行为是由引用或指针引发的,你上面的代码再加上如下的代码:

A *p=&amp;a;
p->print();

会发生了多态;

但至于为什么,编译器是如何做到的,偶也不清楚,坐等高手了;


A *p=&b;
是这样来说明多态吧?
这个我的理解是,因为b的虚函数表里的jmp指令会告诉编译器到哪里调……
[/Quote]

因为你已经执行了:
memcpy(&a,&b,sizeof(a))操作;
ztenv 版主 2010-07-14
  • 打赏
  • 举报
回复
我的理解:当使用类对象的时候,就会在类的存储空间寻找函数地址,并调用函数,所以不会找到派生类的函数地址,而当使用类指针或引用的时候,就会在当前类指针所指对象的类中寻找函数,所以此时会找到类指针所指对象的类的存储空间,所以会发生多态;
james_hw 2010-07-14
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 lianshaohua 的回复:]
因为多态的行为是由引用或指针引发的,你上面的代码再加上如下的代码:

A *p=&a;
p->print();

会发生了多态;

但至于为什么,编译器是如何做到的,偶也不清楚,坐等高手了;
[/Quote]

A *p=&b;
是这样来说明多态吧?
这个我的理解是,因为b的虚函数表里的jmp指令会告诉编译器到哪里调用函数,因此虽然是A类指针,但是编译器查找虚函数表时,其实是b的虚函数表,调用的自然就是b的print函数。
但是这个理解和我上面的描述产生了冲突,因此想不通编译器是如何做到的
ztenv 版主 2010-07-14
  • 打赏
  • 举报
回复
因为多态的行为是由引用或指针引发的,你上面的代码再加上如下的代码:

A *p=&a;
p->print();

会发生了多态;

但至于为什么,编译器是如何做到的,偶也不清楚,坐等高手了;

64,646

社区成员

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

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