【问题标题】:What is the structure of virtual tables in C++?C++中虚表的结构是什么?
【发布时间】:2011-05-03 10:58:40
【问题描述】:


例如,我有两个“接口”和类类型:

class IPlugin
{
  public:
    virtual void Load(void) = 0;
    virtual void Free(void) = 0;
};

class IFoo
{
  public:
    virtual void Foo(void) = 0;
};


class Tester: public IPlugin, public IFoo
{
   public:
           Tester() {};
          ~Tester() {};

           virtual void Load()
           {
              // Some code here
           }

           virtual void Free()
           {
              // Some code here
           }

           virtual void Foo(void)
           {
              // Some code here
           }
 };

vtab 实际上有什么结构,例如Tester 类型? dynamic_cast 运算符如何在表达式中执行(我的意思是 dynamic_cast 运算符将如何扫描 vtab 以进行有效的引用类型转换):

Tester* t = new Tester();
IPlugin* plg = dynamic_cast<IPlugin*>(t);
IFoo* f = dynamic_cast<IFoo*>(plg);  

提前致谢!

【问题讨论】:

  • 虚函数调用的实现在C++标准中没有定义。您不会得到对所有编译器和编译器版本都正确的答案。
  • @DumbCoder:谢谢你的链接,但事实证明我的问题没有共同的答案

标签: c++ vtable


【解决方案1】:

C++ 中的虚拟表是一个实现细节。下图显示了一种可能的实现方式。

存在类的两个实例(A 和 B)。每个实例都有两个 vtbl 指针,而 vtbl 包含指向实际代码的指针。

在您的示例中没有实例数据,但出于说明目的,我假设每个类都包含一些实例数据。

当指向Tester 的指针被转换为指向IFoo 的指针时,指针会如图所示进行调整。它不是指向实例数据的开头,而是指向实例数据的IFoo 部分。

巧妙的是,使用IFoo 指针的调用者对类的IFoo 部分周围的数据一无所知。使用IPlugin 指针的调用者也可以这样说。该指针恰好指向实例数据的开头,也由 Tester 指针指向,但只有使用 Tester 指针的调用者知道实例数据的整个布局。

使用dynamic_cast 需要RTTI(运行时类型信息),它不在图表中。 vtbl 将包含额外的类型信息,给定一个指向 Tester 实例的 IFoo 指针,允许代码在运行时发现指针指向的对象的实际类型并使用它来向下转换指针。

【讨论】:

    【解决方案2】:

    vtab 实际具有什么样的结构,例如 Tester 类型?

    虚拟调度的机制是实现定义的。 vtable 和 vptr 不是 C++ 标准所要求的,程序员在 C++ 中编程甚至不需要这些知识,因为您无法访问虚拟表(即使您的编译器实现了这一点);它由编译器生成并添加到您的代码中,就像它在将代码转换为机器代码之前对您的代码做了很多事情一样。


    Tester* t = new Tester();
    IPlugin* plg = dynamic_cast<IPlugin*>(t);
    IFoo* f = dynamic_cast<IFoo*>(plg);  
    

    这里第二行不需要dynamic_cast。以下内容就足够了:

    Tester* t = new Tester();
    IPlugin* plg = t;                 //upcast          - dynamic_cast not needed
    IFoo* f=dynamic_cast<IFoo*>(plg); //horizontal-cast - dynamic_cast needed
    

    向上转换不需要dynamic_cast;仅在下垂和水平施放时需要。

    Tester* tester1 = dynamic_cast<Tester*>(plg); //downcast - dynamic_cast needed
    Tester* tester2 = dynamic_cast<Tester*>(f);   //downcast - dynamic_cast needed
    

    【讨论】:

    • @Nawaz:还记得一年前的虚拟机制实现细节Q吗? :)
    • @Als:在哪里?哪个话题?我不记得了:-s
    • @Nawaz: Here 检查 OP 和第一位评论者 :)
    • @Nawaz:不,没有意义……只是让我想起过去的美好时光:P
    • 如果你在做windows编程,定义了VTable二进制格式(假设你想做COM工作,.NET可能类似?)。也就是说,虽然 C++ 没有定义会发生什么,但某些操作系统确实定义了 VTable 的确切结构。
    【解决方案3】:

    ISO/IEC 14882 第二版 2003-10-15 中不存在诸如 vptr,虚拟表,所以完全取决于编译器实现者。

    关于 impl 的信息。在微软的视觉 C++ 中: http://www.openrce.org/articles/files/jangrayhood.pdf

    关于 impl 的文章。 g ++中的虚拟表: http://phpcompiler.org/articles/virtualinheritance.html

    【讨论】:

      【解决方案4】:

      C++ 标准没有定义虚拟机制(虚拟指针和虚拟表)。编译器以自己选择的方式实现该机制被忽略了。它是编译器的实现细节。鉴于此,编译器将如何实现虚拟机制的细节从用户那里抽象出来。重要的是虚拟机制中预期的行为。

      在你的情况下:

      Tester* t = new Tester(); 
      IPlugin* plg = dynamic_cast<IPlugin*>(t); 
      IFoo* f = dynamic_cast<IFoo*>(plg);   
      

      plgf 都将指向它们各自类型的有效对象,因为 t 是从它们两者派生的。

      当然,这并不能回答您提出的具体问题,而只是想清楚有关虚拟机制的细节是编译器的实现细节。

      【讨论】:

      • 谢谢!我真的认为标准中指定的 vtab :)
      猜你喜欢
      • 2023-04-02
      • 1970-01-01
      • 2016-01-03
      • 2017-01-02
      • 2011-11-28
      • 1970-01-01
      • 2014-09-01
      • 2011-02-12
      • 1970-01-01
      相关资源
      最近更新 更多