1

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

class A
{
    virtual void foo();
}

我知道foo是虚拟方法表中的第一个 (0) 项

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

4

4 回答 4

1

尽管 vtable 恰好是大多数(可能是所有)C++ 实现实现虚函数分派的方式,但即使在标准中也不能保证它们存在,更不用说它们以特定顺序存在。

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

或者以其他方式使用令人难以置信的特定于平台的技巧来获取成员函数的实际地址,在内存中找到 vtable,然后搜索其中的地址。

但无论哪种方式,此类信息都特定于一个平台和编译器,甚至可能是编译器版本,具体取决于该特定编译器的 ABI 保证。

附带说明一下,GCC 和 MSVC++ 都记录了其 vtable 的布局算法,并记录了 vptr 在对象中的位置的算法。对于 GCC,文档是 Common C++ ABI(又名 Itanium C++ ABI)。对于 MSVC++,我不知道文档在哪里或者它是否直接存在,但是编译器保证至少没有数据成员的类被布置为与 COM ABI 兼容。

于 2016-09-18T09:11:24.290 回答
1

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

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

于 2016-09-18T09:00:18.597 回答
0

理想情况下,我们可以循环函数地址并比较指定函数的地址,我用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;
}

下图是 test::goo 的地址和 f2 的值(存储 test::goo 的正确地址) 在此处输入图像描述

于 2016-09-18T11:03:27.883 回答
0

您似乎想以一般方式进行虚拟调度,只知道函数签名,并以其他方式获取该函数引用。
虽然这种追求可能有好处,但您的问题将它与实现细节(vtable)混为一谈,C++ 标准不保证这一点。

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

#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;
}
于 2016-10-02T06:45:07.057 回答