首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >vtables和此指针

vtables和此指针
EN

Stack Overflow用户
提问于 2012-06-23 23:51:20
回答 1查看 2.4K关注 0票数 3

我试图了解更多关于vtable和vpointer的内部工作原理,所以我决定尝试使用一些技巧直接访问vtable。我创建了两个类,BaseDerv,每个类都有两个virtual函数(Derv覆盖了Base的函数)。

代码语言:javascript
复制
class Base
{
    int x;
    int y;

    public:
        Base(int x_, int y_) : x(x_), y(y_) {}

        virtual void foo() { cout << "Base::foo(): x = " << x << '\n'; }    
        virtual void bar() { cout << "Base::bar(): y = " << y << '\n'; }
};

class Derv: public Base
{
    int x;
    int y;

    public:
        Derv(int x_, int y_) : Base(x_, y_), x(x_), y(y_) {}

        virtual void foo() { cout << "Derived::foo(): x = " << x << '\n'; }
        virtual void bar() { cout << "Derived::bar(): y = " << y << '\n'; }
};

现在,编译器向每个类添加一个vtable指针,占用内存中的前4个字节(32位)。由于该指针指向另一个大小为sizeof(size_t)的指针,因此我通过将对象的地址转换为size_t*来访问该指针。现在可以通过索引vpointer并将结果转换为适当类型的函数指针来访问虚函数。我将这些步骤封装在一个函数中:

代码语言:javascript
复制
template <typename T>
void call(T *ptr, size_t num)
{
    typedef void (*FunPtr)();

    size_t *vptr = *reinterpret_cast<size_t**>(ptr);
    FunPtr fun = reinterpret_cast<FunPtr>(vptr[num]);

    //setThisPtr(ptr);      added later, see below!
    fun();
}

当其中一个成员函数以这种方式调用时,例如调用Base::foo()的call(new Base(1, 2), 0),很难预测会发生什么,因为它们是在没有this-pointer的情况下调用的。我通过添加一个小的模板化函数解决了这个问题,因为我知道g++将this-pointer存储在ecx寄存器中(但是这迫使我使用-m32编译器标志进行编译):

代码语言:javascript
复制
template <typename T>
void setThisPtr(T *ptr)
{  
    asm ( mov %0, %%ecx;" :: "r" (ptr) );
}

取消上面代码段中的setThisPtr(ptr)行的注释现在使它成为一个工作程序:

代码语言:javascript
复制
int main()
{
    Base* base = new Base(1, 2);
    Base* derv =  new Derv(3, 4);

    call(base, 0); // "Base::foo(): x = 1" 
    call(base, 1); // "Base::bar(): y = 2"
    call(derv, 0); // "Derv::foo(): x = 3"
    call(derv, 1); // "Derv::bar(): y = 4"
}

我决定分享这篇文章,因为在写这个小程序的过程中,我对vtable是如何工作的有了更多的了解,它可能会帮助其他人更好地理解这些材料。然而,我仍然有一些问题:

  1. 编译64位二进制文件时,使用哪个寄存器(gcc 4.x)来存储此指针?我尝试了所有64位寄存器,如下所示:http://developers.sun.com/solaris/articles/asmregs.html
  2. When/how是这个指针集吗?我怀疑编译器在通过对象的每个函数调用上设置this指针的方式与我刚才所做的类似。这就是多态性的实际工作方式吗?(首先设置this指针,然后从vtable调用虚拟函数?)。
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-08-15 13:58:14

在Linux x86_64上,以及我相信其他类UNIX的OSes上,函数调用遵循System V ABI (AMD64),它本身遵循C++的IA-64 C++ ABI。根据方法的类型,this指针要么通过第一个参数隐式传递,要么通过第二个参数隐式传递(当返回值具有非平凡的复制构造函数或析构函数时,它必须作为临时堆栈存在,而第一个参数隐式地是指向该空间的指针);否则,虚方法调用与C中的函数调用相同( %rdi%rsi%rdx%rcx%r8%r9中的整数/指针参数,溢出到堆栈;%rax中的整数/指针返回;%xmm0中的浮点-<代码>d11;等等)。虚方法分派的工作原理是在vtable中查找指针,然后像调用非虚方法一样调用它。

我不太熟悉Windows的x64约定,但我相信它的相似之处在于,C++方法调用遵循与C函数调用完全相同的结构(使用与Linux上不同的寄存器),只是首先使用了一个隐式的this参数。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11170868

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档