zoukankan      html  css  js  c++  java
  • C++编译器检索VTABLE的具体方法不同

    以前没太注意过这个问题,只知道C++代码调用某个虚函数时要到虚函数表里去查找,然后执行特定的函数。这两天跟别人调程序时发现,VC和gcc对这个检索操作的实现方法是不一样的:

    早期的gcc(2001年5月前?)使用的是一个比较直观的查表方法。gcc中指向成员函数的指针其实是一个结构,类似于最早期的cfront编译器:

    struct
    {
    ??? short __delta;
    ??? short __index;
    ??? union
    ??? {
    ??????? void * __pfn;
    ??????? short _delta2;
    ??? } _pfn_or_delta2;
    };

    如果指向非虚成员函数,__pfn中就保存了内存中实际的函数指针。如果指向虚成员函数,__pfn无效,__index为该函数在虚函数表中的索引。调用虚函数时,根据虚函数表的地址和索引查到特定函数地址。

    后续版本的gcc在不断改进这种虚函数表的检索方法(实际上后续的gcc对C++ ABI的改动比较多),现在的gcc 3.3.3的源码里,gcc/cp/cp-tree.h等文件包含了相关代码。不过现在的实现代码比较复杂,我还没有彻底看明白。

    而VC的实现与此大不相同。VC为一棵继承树上的每个虚函数生成一个vcall代码(可以被看成一个小的中介函数),根据每个虚函数在虚函数表中的偏移不同,每个vcall调用时为eax增加的偏移量不同。这实际上是一种快速的查表机制,而且不用传虚函数表的index就可以调用到特定函数了。

    `vcall':
    00401010? mov???????? eax,dword ptr [ecx]
    00401012? jmp???????? dword ptr [eax]
    `vcall':
    00401020? mov???????? eax,dword ptr [ecx]
    00401022? jmp???????? dword ptr [eax+4]
    `vcall':
    00401030? mov???????? eax,dword ptr [ecx]
    00401032? jmp???????? dword ptr [eax+8]
    `vcall':
    00401040? mov???????? eax,dword ptr [ecx]
    00401042? jmp???????? dword ptr [eax+0Ch]

    调用时,对同名虚函数(无论该函数在属于一个类),使用的都是同一个vcall,但调用不同类的虚函数时,传入vcall的虚函数表起始地址不同(下面C2类是C1类的派生类,f1和f2是虚函数)。

    & C1::f1 - offset `vcall' (401010h)
    & C1::f2 - offset `vcall' (401020h)
    & C2::f1 - offset `vcall' (401010h)
    & C2::f2 - offset `vcall' (401020h)

  • 相关阅读:
    Codeforces 451A Game With Sticks
    POJ 3624 Charm Bracelet
    POJ 2127 Greatest Common Increasing Subsequence
    POJ 1458 Common Subsequence
    HDU 1087 Super Jumping! Jumping! Jumping!
    HDU 1698
    HDU 1754
    POJ 1724
    POJ 1201
    CSUOJ 1256
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157307.html
Copyright © 2011-2022 走看看