gpt4 book ai didi

c++ - vtables 和 this 指针

转载 作者:太空狗 更新时间:2023-10-29 21:04:45 26 4
gpt4 key购买 nike

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

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 位)。我通过将对象的地址转换为 size_t* 来访问此指针,因为该指针指向另一个大小为 sizeof(size_t) 的指针。现在可以通过索引 vpointer 并将结果转换为适当类型的函数指针来访问虚函数。我将这些步骤封装在一个函数中:

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();
}

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

template <typename T>
void setThisPtr(T *ptr)
{
asm ( mov %0, %%ecx;" :: "r" (ptr) );
}

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

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"
}

我决定分享这个,因为在编写这个小程序的过程中,我对 vtables 的工作原理有了更多的了解,这可能会帮助其他人更好地理解这些 Material 。但是我还有一些问题:
1. 在编译 64 位二进制文​​件时,使用哪个寄存器 (gcc 4.x) 来存储 this 指针?我尝试了此处记录的所有 64 位寄存器:http://developers.sun.com/solaris/articles/asmregs.html
2. 何时/如何设置this-pointer?我怀疑编译器在通过一个对象进行的每个函数调用上都设置了 this 指针,其方式与我刚才的做法类似。这是多态性实际工作的方式吗? (首先设置 this 指针,然后从 vtable 调用虚函数?)。

最佳答案

在 Linux x86_64 和我相信其他类 UNIX 操作系统上,函数调用遵循 System V ABI (AMD64) , 它本身遵循 IA-64 C++ ABI对于 C++。根据方法的类型,this 指针通过第一个参数或第二个参数隐式传递(当返回值具有非平凡的复制构造函数或析构函数时,它必须作为临时堆栈存在,并且第一个参数是隐式指向该空间的指针);否则,虚方法调用与 C 中的函数调用相同(%rdi%rsi%rdx%rcx%r8%r9,溢出入栈;整数/指针在%rax中返回;在中 float code>%xmm0-%xmm7; 等)。虚拟方法调度通过在 vtable 中查找指针然后像调用非虚拟方法一样调用它来工作。

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

关于c++ - vtables 和 this 指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11170868/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com