通过虚拟指针访问成员函数的指针

问题描述 投票:3回答:2

我遇到过文章,他们解释了vptr和vtable。我知道在存储虚函数的类的情况下,对象中的第一个指针是vptr到vtable,而vtable的数组条目是指向函数的指针,它们在类中出现的顺序相同(我已经通过我的测试验证了)程序)。但我试图理解必须编译器放置什么语法才能调用适当的函数。

例:

class Base
 {
   virtual void func1() 
   { 
       cout << "Called me" << endl; 
   }
};
int main()
{
  Base obj;
  Base *ptr;
  ptr=&obj;

// void* is not needed. func1 can be accessed directly with obj or ptr using vptr/vtable
  void* ptrVoid=ptr; 

// I can call the first virtual function in the following way:
  void (*firstfunc)()=(void (*)(void))(*(int*)*(int*)ptrVoid); 
  firstfunc();
}

问题:

但我真正想要了解的是编译器如何用ptr->func1()替换对vptr的调用?如果我要模拟电话,那我该怎么办?我应该重载->运算符。但即便如此也无济于事,因为我不知道func1的名字到底是什么。即使他们说编译器通过vptr访问vtable,它仍然知道func1的条目是func2的第一个数组adn条目是数组中的第二个元素吗?必须将函数名称映射到数组元素。

2.我该如何模拟它。你能提供编译器用来调用函数func1的实际语法(它如何取代ptr->func1())?

c++ virtual-functions vtable vptr
2个回答
2
投票

不要将vtable视为数组。它只是一个数组,如果你除去它的成员大小以外的C ++所知道的一切。相反,把它想象成第二个struct,其成员都是功能的指针。

假设我有一个这样的类:

struct Foo {
    virtual void bar();
    virtual int baz(int qux);
    int quz;
}

int callSomeFun(Foo* foo) {
    foo->bar();
    return foo->baz(2);
}

分解一步:

class Foo;
// adding Foo* parameter to simulate the this pointer, which
// in the above would be a pointer to foo.
struct FooVtable {
    void (*bar)(Foo* foo);
    int (*baz)(Foo* foo, int qux);
}
struct Foo {
    FooVtable* vptr;
    int quz;
}

int callSomeFun(Foo* foo) {
    foo->vptr->bar(foo);
    return foo->vptr->baz(foo, 2);
}

我希望这就是你要找的东西。


0
投票

的背景:

  1. 编译后(没有调试信息)C / C ++的二进制文件没有名称,运行时工作不需要名称,它只有机器代码
  2. 您可以考虑类似于clasic C函数指针的vptr,在某种意义上,类型,参数列表等是已知的。
  3. 放置func1,func2等位置并不重要,只需要命令总是相同的(因此多文件C ++的所有部分必须以相同的方式编译,编译器设置等)。让我们想象一下,位置是在声明顺序,FIRST父类,然后在重写中新声明BUT重新实现的虚拟位于较低位置,如来自父级。

它唯一的形象。实施必须正确触发覆盖classApionter->methodReimplementedInB()

  1. 通常C ++编译器已经拥有(我的知识来自16 / 32b迁移)2-4选项来优化vtable与速度/大小等。经典C sizeof()非常好理解(数据大小加上ev。对齐),在C ++中sizeof更大,但可以保证它是2,4,8字节。

4很少有转换工具可以转换“对象”文件,即从MS格式转换为Borland等,但由于vtable的未知机器代码实现,通常/只有经典C是可能的/安全的。

  1. 难以触及高级代码的vtable,中间文件的火灾分析器(.obj,等)

编辑:关于运行时的故事与编译不同。我的答案是关于编译代码和运行时

EDIT2:准汇编代码(来自我的脑海)

load ax, 2
call vt[ax]

vt:
0x123456
0x126785  // virlual parent func1()

衍生:

vt:
0x123456
0x126999 // overriden finc1()
0x456788 // new method

编辑3:BTW我不能完全同意C ++总是更好地加速JVM / .NET,因为“这些被解释”。 C ++有“intepretation”的一部分,解释部分是groving:真正的组件/ GUI框架也解释了之间的连接(例如map)。在我们的讨论中:使用C ++ delete还是GC,哪种内存模型更好?

© www.soinside.com 2019 - 2024. All rights reserved.