【问题标题】:Virtual table/dispatch table虚拟表/调度表
【发布时间】:2011-09-30 04:37:41
【问题描述】:

根据我对 CPP 的了解,每个类都有自己的 vtable。

但是this维基百科链接提到:

一个对象的调度表将 包含对象的地址 动态绑定的方法。方法 调用是通过获取 方法的地址来自对象的 调度表。调度表是 对于属于的所有对象都相同 同一类,因此是 通常在他们之间共享。

有人可以解释一下吗?

谢谢!

【问题讨论】:

  • 注意 - 并非 C++ 中的所有类都有 vtables。只有具有虚拟功能的才有。当没有虚函数时,所有方法都可以静态绑定(编译时),运行时不需要dispatch机制。

标签: java c++ oop virtual-functions


【解决方案1】:

有时举个例子更容易理解:

class PureVirtual {
   public:
       virtual void methodA() = 0;
       virtual void methodB() = 0;
};

class Base : public PureVirtual {
   public:
        virtual void methodA();
        void methodC();
   private:
        int x;
 };

 class Derived : public Base {
    public:
         virtual void methodB();
    private:
         int y;
 };

所以,给定一个 Derived 类型的对象,它可能看起来像:

                         ------------
 Known offset for vtable |  0xblah  | -------> [Vtable for type "Derived"]
                         ------------
 Known offset for x      |  3       |
                         ------------
 Known offset for y      |  2       |
                         ------------

“派生”类型的 Vtable 看起来像:

                            ------------
 Known offset for "methodA" | 0xblah1   | ------> methodA from Base
                            -------------
 Known offset for "methodB" | 0xblah2   | ------> methodB from Derived
                            -------------

请注意,由于“methodC”不是虚拟的,所以它根本不在 vtable 中。另请注意,Derived 类的所有实例都有一个指向同一个共享 vtable 对象的 vtable 指针(因为它们具有相同的类型)。

虽然 C++ 和 Java 的实现略有不同,但它们的想法并非不兼容。从概念的角度来看,主要区别在于 Java 方法是“虚拟的”,除非声明为“最终”。在 C++ 中,必须明确给出关键字“virtual”才能使函数位于 vtable 中。任何不在 vtable 中的东西都将使用编译时类型而不是对象的运行时类型进行调度。

【讨论】:

    【解决方案2】:

    每个具有虚函数的类(即在 Java 中只是“每个类”)都有自己的vtable。每个对象都隐藏了对其类 vtable 的引用。因此,同一类的对象具有相同的引用。

    当你调用虚方法时,编译器通常会进行查找:

    obj.method(args);
    

    被翻译成某种东西

    obj.vtable[idx_method](obj, args);
    

    有时,如果编译器可以推断出特定类型的对象,它可以发出静态调用而不是虚拟调用。所以,代码

    MyObject obj(ctor_args);
    ....
    obj.method(args);
    

    可以翻译成

    MyObject_method(obj, args);
    

    这通常比虚拟调用执行得更快。

    【讨论】:

      【解决方案3】:

      是的,编译器和运行时对虚拟方法的处理方式不同。

      Java : 默认情况下,java 中的所有方法都是虚拟的。这意味着在继承中使用任何方法都可以被覆盖,除非该方法声明为 final 或 static。

      来自VM Specification

      Java 虚拟机没有 授权任何特定的内部 对象的结构。书签 那里指出:在一些 Sun 的 Java虚拟的实现 机器,一个类的引用 instance 是一个指向句柄的指针 本身是一对指针:一个到一个 包含方法的表 对象和指向类的指针 表示类型的对象 对象,另一个到内存 从堆中为对象分配 数据。


      C++:

      每当一个类成员函数被声明为虚函数时,编译器都会创建一个 内存中的虚拟表,其中包含声明为的所有函数指针 在那个类中是虚拟的。这可以实现运行时多态性(即找出所需的 运行时的函数)。虚函数表在 反对 vtable。由于这个额外的指针和 vtable 增加了 对象,类设计者需要谨慎地声明虚拟函数。

      在基对象指针上调用方法时的事件顺序是:

      • 获取vtable指针(这个vtable指针指向vtable的开头)。
      • 使用偏移量获取 vtable 中的函数指针。

      通过vtable指针间接调用函数。

      【讨论】:

        【解决方案4】:

        这句话指出每个对象都有一个调度表,可以在类之间共享,因为它们对于同一类的所有实例都是相同的。 IE。每个类都有自己的 vtable。

        【讨论】:

        • thnx .. 你的意思是反过来 .. 每个类都有一个由它的实例[objects]共享的调度表
        猜你喜欢
        • 1970-01-01
        • 2015-12-15
        • 2011-01-11
        • 1970-01-01
        • 2011-04-27
        • 1970-01-01
        • 2019-06-12
        • 2011-01-25
        相关资源
        最近更新 更多