C ++获取虚函数表索引

时间:2016-09-18 08:41:56

标签: c++ function indexing virtual vmt

是否有可能(以及如何)在虚拟方法表中获取虚函数的索引?

class A
{
    virtual void foo();
}

我知道foo是虚方法表

中的第一个(0)项

但是,我可以foo并获得0吗?

4 个答案:

答案 0 :(得分:1)

正如我在评论中所说,我怀疑它是实现定义的一个实现细节(感谢@SteveJessop在答案的评论中指出了正确的术语)并依赖于ABI,所以(理论上)以便携的方式是不可能的 作为已知ABI定义的示例,请参阅here(Itanium C ++ ABI) 换句话说,这是C ++标准中的术语,意思是 - 实现必须记录它
而且,正如@n.m.在问题的评论中提到,该标准不包含任何类似 vtable 的内容,因此几乎没有明确规定。
实现是否可以自由使用它们,如果它们使用它们,它们可以自由地为用户代码提供支持的方式来访问它们。

再说一遍:没有明确的,可移植的方式来做到这一点。

答案 1 :(得分:1)

尽管vtable碰巧是大多数(可能是所有)C ++实现实现虚函数调度的方式,但它们甚至不能保证它们存在于标准中,更不用说它们是按特定顺序存在的。

换句话说,唯一的方法就是采用你的特定编译器,找出它如何排列vtable,然后根据类布局遵循相同的算法来找到你感兴趣的函数的索引。

或以其他方式使用难以置信的特定于平台的黑客来获取成员函数的实际地址,在内存中找到vtable,并搜索内部的地址。

但不管怎样,这些信息特定于一个平台和编译器,甚至可能是编译器版本,具体取决于该特定编译器的ABI保证。

作为旁注,GCC和MSVC ++都为其vtable记录了布局算法,并记录了vptr在对象中所处位置的算法。对于GCC,文档是Common C ++ ABI(a.k.a.Itanium C ++ ABI)。对于MSVC ++,我不知道文档的位置或它是否直接存在,但编译器保证至少没有数据成员的类被布置为与COM ABI兼容。

答案 2 :(得分:0)

您似乎希望以一般方式进行虚拟调度,只知道函数签名,并以其他方式获取该函数引用。
虽然这种追求可能有价值,但您的问题与实施细节(vtable)相混淆,而C ++标准无法保证这一点。

幸运的是,该标准提供了一种在不知道其名称的情况下调用成员函数的方法,它甚至尊重虚拟调度。它是一个指向成员的简单指针(您可以存储和复制)。你使用它like this

#include <iostream>

struct A
{
    virtual void foo() { std::cout << "A::foo\n"; } 
};

struct AA : A
{
    virtual void foo() { std::cout << "AA::foo\n"; } 
};

struct AAA : AA
{
    virtual void foo() { std::cout << "AAA::foo\n"; } 
};

void bar (A& a, void (A::* pMem)())
{
    (a.*pMem)();
}

int main() {
    A a;
    AA aa;
    AAA aaa;

    bar (a, &A::foo);
    bar (aa, &A::foo);
    bar (aaa, &A::foo);

    return 0;
}

答案 3 :(得分:-1)

理想情况下,我们可以循环函数地址并比较指定函数的地址,我在g ++中编写了以下代码,可以输出虚函数的索引。

#include <iostream>
#include <stdio.h>
using namespace std;

class test {
public:
  virtual void foo() {
    cout << "foo" << endl;
  }
  virtual void goo() {
    cout << "goo" << endl;
  }
  virtual void hoo() {
    cout << "hoo" << endl;
  }
};

int find_function_index(test* p, void* f) {
  for(int i = 0; i < 3; ++i) {
    void* tmp = (void*)*((long*)*(int*)(p)+i);
    if(tmp == f)
      return i;
  }
  return -1;
}

int main() {
  test* p = new test();

  void* f1 = reinterpret_cast<void*>(&test::foo);
  cout << "foo: " << find_function_index(p, f1) << endl;

  void* f2 = reinterpret_cast<void*>(&test::goo);
  cout << "goo: " << find_function_index(p, f2) << endl;

  void* f3 = reinterpret_cast<void*>(&test::hoo);
  cout << "hoo: " << find_function_index(p, f3) << endl;
}

以下pic是test :: goo的地址和f2的值(存储test :: goo的正确地址) enter image description here