c++ - 为什么使用存储在虚方法表中的地址调用虚函数会返回垃圾?

标签 c++ c++14 pointer-to-member virtual-functions virtual-table

我正在从虚拟表中的地址调用虚拟函数作为练习来测试我对这个概念的理解。然而,当我以为我对虚方法表的理解有了突破时,我又遇到了一个我就是不明白的问题。

在下面的代码中,我创建了一个名为 Car 的类,它包含一个成员变量 x 和两个虚函数,first 和 second。现在,我通过虚拟表来调用这两个虚拟方法。第一个函数返回正确答案,但第二个函数返回一些随机值或垃圾而不是它被初始化的值。

#include <cstdio>

class Car
{
private:
    int x;

    virtual int first()
    {
        printf("IT WORKS!!\n");
        int num = 5;
        return num;
    }
    virtual int second()
    {
        printf("IT WORKS 2!!\n");
        //int num  = 5;
        return x;
    }


public:

    Car(){
        x = 2;
    }
};

int main()
{
    Car car;
    void* carPtr = &car;
    long **mVtable =(long **)(carPtr);

    printf("VTable: %p\n", *mVtable);
    printf("First Entry of VTable: %p\n", (void*) mVtable[0][0]);
    printf("Second Entry of VTable: %p\n", (void*) mVtable[0][1]);

    if(sizeof(void*) == 8){
        printf("64 bit\n");
    }

    int (*firstfunc)() = (int (*)()) mVtable[0][0];
    int x = firstfunc();    

    int (*secondfunc)() = (int (*)()) mVtable[0][1];
    int x2 = secondfunc();

    printf("first: %d\nsecond: %d", x, x2);
    return 0;
}

如果有人能指出我做错了什么,我们将不胜感激。此外,由于这在编译器之间的工作方式不同,我在 http://cpp.sh/ 上测试它使用 C++14。

代码输出,其中“垃圾”第二个输出可能会发生变化:

VTable: 0x400890
First Entry of VTable: 0x400740
Second Entry of VTable: 0x400720
64 bit
IT WORKS!!
IT WORKS 2!!
first: 5
second: -888586240 

最佳答案

方法确实通常作为常规函数实现,但它们需要接收 this 指针以访问特定实例的数据 - 事实上,当您通过实例调用方法时,指针指向该实例作为隐藏参数传递。

在您的代码中,您没有传入它,因此该方法只是返回垃圾——它可能正在使用寄存器或堆栈中的任何内容,就好像它是实例指针一样;你很幸运,它没有明显崩溃。

您可以尝试更改您的原型(prototype)以接受 Car* 参数并将 &car 传递给它,但它可能会或可能不会工作,具体取决于使用的调用约定你的编译器/平台:

  • 例如,在 Win32/x86/VC++ 上,方法使用 stdcall 调用约定(或用于可变参数的 cdecl),但接收 this ecx 中的指针,这是您无法通过常规函数调用模拟的东西;
  • 另一方面,x86 gcc 只是将它们作为cdecl 函数来处理,隐式传递this,就好像它是最后一个参数一样。

关于c++ - 为什么使用存储在虚方法表中的地址调用虚函数会返回垃圾?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52209850/

相关文章:

c++ - 在没有 typedef 的情况下使用时显示编译错误的函数的 volatile 指针;需要帮助 w/"void (* volatile userFunc)(void)"

c++ - 如何防止我的程序打印额外的斜杠?

c++ - 方法定义推导的类型与声明不匹配

c++ - 复制分配相同类型的 C++ lambda

C++模板指向成员

C++成员函数指针指向全局函数指针

c++ - 在 C++ 中比较两个数组的 2 个元素

c++ - 在 C++ 中调用 (date --set) 时避免 system() 输出

c++ - 更新功能中的跳跃 Action 播放声音(openframework)

c++ - 不复制就返回std::function