justinli79 发表于 12-5-2009 17:49:14

继承中的虚函数表(virtual function pointer table )

今天看了一段代码,有点疑问
#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()的时候,编译器是怎么来做的?

coredump 发表于 12-5-2009 18:39:48

http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_Drawing1.jpg
对于子类实例中的虚函数表,是下面这个样子:
http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_vtable4.JPG
我们可以看到:
1) 每个父类都有自己的虚表。
2) 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)


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

justinli79 发表于 12-5-2009 19:06:46

coredump你说的和我说的不是一问题。
你的类里面没有重载,Derive里面没有重载任一个Base里面的函数
在我的class是单继承,所以d只有一个虚表,而且从内存里面看,虚表里面只有一个entry,指向了Drive::f()

你给的文章里面给了两个图:
http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_Drawing4.jpg
http://p.blog.csdn.net/images/p_blog_csdn_net/haoel/15190/o_vtable3.JPG

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

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

coredump 发表于 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我不是很清楚,也许是和编译器的具体实现有关。

coredump 发表于 12-5-2009 19:59:43

原帖由 justinli79 于 12-5-2009 18:06 发表 http://www.freeoz.org/forum/images/common/back.gif 可以看到Derive::f覆盖了Base::f,Base::f消失了,咋办? Base::f消失了很正常,而且必须消失,否则怎么体现多态呢?这时候如果还想在Derived类中调用Base::f必须使用全限定名 (也就是用this->Base::f()这样的方式调用,这时候走的就不是虚函数表了,是普通的函数调用).

key 发表于 12-5-2009 20:49:06

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

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

justinli79 发表于 12-5-2009 21:04:14

原帖由 coredump 于 12-5-2009 16:59 发表 http://freeoz.org/forum/images/common/back.gif
Base::f消失了很正常,而且必须消失,否则怎么体现多态呢?这时候如果还想在Derived类中调用Base::f必须使用全限定名 (也就是用this->Base::f()这样的方式调用,这时候走的就不是虚函数表了,是普通的函数调用).
了解,谢谢!

justinli79 发表于 12-5-2009 21:06:22

回复 #6 key 的帖子

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

我以前用visio画图。

coredump 发表于 12-5-2009 21:41:29

原帖由 key 于 12-5-2009 19:49 发表 http://www.freeoz.org/forum/images/common/back.gif
小弟少见多怪,你们的图是用什么工具画的?谢谢

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

key 发表于 12-5-2009 22:37:00

原帖由 justinli79 于 12-5-2009 20:04 发表 http://www.freeoz.org/forum/images/common/back.gif

了解,谢谢!

我想下图可能更有利于说明这种指针指向关系。图的出处在: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 编辑 ]

justinli79 发表于 13-5-2009 13:05:48

受教了!
谢谢各位大大

key 发表于 13-5-2009 13:50:31

原帖由 justinli79 于 13-5-2009 12:05 发表 http://www.freeoz.org/forum/images/common/back.gif
受教了!
谢谢各位大大

共同学习,一起进步,hohoho~
页: [1]
查看完整版本: 继承中的虚函数表(virtual function pointer table )