找回密码
 FreeOZ用户注册
查看: 2011|回复: 11
打印 上一主题 下一主题

[论坛技术] 继承中的虚函数表(virtual function pointer table )

[复制链接]
跳转到指定楼层
1#
发表于 12-5-2009 17:49:14 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?FreeOZ用户注册

x
今天看了一段代码,有点疑问
#include <iostream>
using namespace std;

class Base {
public:
    Base() {
        cout << "In Base" << endl;
        cout << "Virtual Pointer = " << (int*)this << endl;
        cout << "Address of Vtable = " << (int*)*(int*)this << endl;
        cout << "Value at Vtable = " << (int*)*(int*)*(int*)this << endl;
        cout << endl;
    }
    virtual void f1() { cout << "Base::f1" << endl; }
};

class Drive : public Base {
public:
    Drive() {
        cout << "In Drive" << endl;
        cout << "Virtual Pointer = " << (int*)this << endl;
        cout << "Address of Vtable = " << (int*)*(int*)this << endl;
        cout << "Value at Vtable = " << (int*)*(int*)*(int*)this << endl;
        cout << endl;
    }
    virtual void f1() { cout << "Drive::f2" << endl; }
};

int main() {
    Drive d;
    return 0;
}
打印出来的结果是这样的:
In Base
Virtual Pointer = 0012FE00
Address of Vtable = 004676D8
Value at Vtable = 00421852

In Drive
Virtual Pointer = 0012FE00
Address of Vtable = 004676C8
Value at Vtable = 004220F9

在调用Drive的构造函数的时候,一个新的虚表被创建,里面的函数指针指向Drive的实现。问题来了,Base的虚表在哪里?怎么样可以找到它,在调用Base::f1()的时候,编译器是怎么来做的?
回复  

使用道具 举报

2#
发表于 12-5-2009 18:39:48 | 只看该作者

                               
登录/注册后可看大图

对于子类实例中的虚函数表,是下面这个样子:

                               
登录/注册后可看大图

我们可以看到:
1) 每个父类都有自己的虚表。
2) 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)


FROM:http://www.cppblog.com/xczhang/archive/2008/01/20/41508.html
回复  

使用道具 举报

3#
 楼主| 发表于 12-5-2009 19:06:46 | 只看该作者
coredump你说的和我说的不是一问题。
你的类里面没有重载,Derive里面没有重载任一个Base里面的函数
在我的class是单继承,所以d只有一个虚表,而且从内存里面看,虚表里面只有一个entry,指向了Drive::f()

你给的文章里面给了两个图:

                               
登录/注册后可看大图


                               
登录/注册后可看大图


可以看到Derive::f覆盖了Base::f,Base::f消失了,咋办?

[ 本帖最后由 justinli79 于 12-5-2009 16:09 编辑 ]
回复  

使用道具 举报

4#
发表于 12-5-2009 19:47:35 | 只看该作者

回复 #3 justinli79 的帖子

我之所以给你上面那个例子其实可以更好的解答你的疑惑。单继承情况下Base的VTable并没有消失,而是Base的Vtable保留而Derived VTable和Base共用一个。而这只是多继承的一个特例。
至于为什么在Base的构造函数中看到的VTable地址和Derived的构造函数中看到的不一样的问题是因为构造函数的特殊性决定的,其实在构造函数中是不能调用任何虚函数的,也就是说在构造函数运行时,虚函数表还没有被初始化完成,因为没有初始化完成,所以这时候得到的VTable地址就可能是假的。

在上例中,可以在Base::Base()中调用f1(),但是不要期望而到虚函数的效果,因为Derived在这时候还不存在。当Derived初始化时,到底是沿用Base类的VTable还是摧毁然后构造新的Derived的VTable我不是很清楚,也许是和编译器的具体实现有关。
回复  

使用道具 举报

5#
发表于 12-5-2009 19:59:43 | 只看该作者
原帖由 justinli79 于 12-5-2009 18:06 发表 可以看到Derive::f覆盖了Base::f,Base::f消失了,咋办?
Base::f消失了很正常,而且必须消失,否则怎么体现多态呢?这时候如果还想在Derived类中调用Base::f必须使用全限定名 (也就是用this->Base::f()这样的方式调用,这时候走的就不是虚函数表了,是普通的函数调用).
回复  

使用道具 举报

6#
发表于 12-5-2009 20:49:06 | 只看该作者

题外问题:你们的图是用什么画的?

小弟少见多怪,你们的图是用什么工具画的?谢谢
回复  

使用道具 举报

7#
 楼主| 发表于 12-5-2009 21:04:14 | 只看该作者
原帖由 coredump 于 12-5-2009 16:59 发表
Base::f消失了很正常,而且必须消失,否则怎么体现多态呢?这时候如果还想在Derived类中调用Base::f必须使用全限定名 (也就是用this->Base::f()这样的方式调用,这时候走的就不是虚函数表了,是普通的函数调用).

了解,谢谢!
回复  

使用道具 举报

8#
 楼主| 发表于 12-5-2009 21:06:22 | 只看该作者

回复 #6 key 的帖子

引用的别人的帖子,不知道他用什么画图的。

我以前用visio画图。
回复  

使用道具 举报

9#
发表于 12-5-2009 21:41:29 | 只看该作者
原帖由 key 于 12-5-2009 19:49 发表
小弟少见多怪,你们的图是用什么工具画的?谢谢


别人的, 我现在偶尔用用netbeans画UML,大部分时间是白纸 or 白板
回复  

使用道具 举报

10#
发表于 12-5-2009 22:37:00 | 只看该作者
原帖由 justinli79 于 12-5-2009 20:04 发表

了解,谢谢!


我想下图可能更有利于说明这种指针指向关系。图的出处在:http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/
VTable是用来存放函数指针值的,一旦Base::f()被Derived::f()取代了它在VTable中的位置,就只能通过类似:
x.B::f()
这样的方式来调用,其寻址方式就是给定门牌号码的找。而VTable则是给了一个有优先级的链接,形成了一个shortcut

VTable也很好的解释了C++怎样应对多继承中的歧义问题。

[ 本帖最后由 key 于 12-5-2009 21:40 编辑 ]

评分

参与人数 1威望 +30 收起 理由
coredump + 30 谢谢分享!

查看全部评分

回复  

使用道具 举报

11#
 楼主| 发表于 13-5-2009 13:05:48 | 只看该作者
受教了!
谢谢各位大大
回复  

使用道具 举报

12#
发表于 13-5-2009 13:50:31 | 只看该作者
原帖由 justinli79 于 13-5-2009 12:05 发表
受教了!
谢谢各位大大


共同学习,一起进步,hohoho~
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+11, 24-2-2025 09:05 , Processed in 0.028374 second(s), 31 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表