c++ - 为什么使用存储在虚方法表中的地址对虚函数的函数调用返回垃圾?
问题描述
我从虚拟表中的地址调用虚拟函数作为练习来测试我对这个概念的理解。然而,当我以为我对虚拟方法表的理解有所突破时,我又遇到了另一个我只是不明白的问题。
在下面的代码中,我创建了一个名为的类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;
}
如果有人能指出我做错了什么,那将不胜感激。此外,由于这在编译器中的工作方式不同,我正在使用 c++14在http://cpp.sh/上对其进行测试。
该代码输出输出,其中“垃圾”第二个输出可能会发生变化:
VTable: 0x400890
First Entry of VTable: 0x400740
Second Entry of VTable: 0x400720
64 bit
IT WORKS!!
IT WORKS 2!!
first: 5
second: -888586240
解决方案
方法确实通常作为常规函数实现,但它们需要接收this
指针以访问特定实例的数据 - 事实上,当您在实例上调用方法时,指向实例的指针作为隐藏参数传递。
在您的代码中,您没有将其传入,因此该方法仅返回垃圾-它可能正在使用发生在寄存器或堆栈中的任何内容,就好像它是实例指针一样;你很幸运,它没有明显崩溃。
您可以尝试更改原型以接受Car*
参数并将其传递&car
给它,但它可能会或可能不会起作用,具体取决于您的编译器/平台使用的调用约定:
- 例如,在 Win32/x86/VC++ 上,方法使用
stdcall
调用约定(或cdecl
用于可变参数),但接收this
指针 inecx
,这是无法通过常规函数调用模拟的; - 另一方面,x86 gcc 只是将它们作为
cdecl
函数处理,this
隐式传递,就好像它是最后一个参数一样。
推荐阅读
- ios - 库版本不兼容:AwaitKit 需要 1.0.0 或更高版本,但 libswiftCore.dylib 提供 0.0.0 版本
- unity3d - unity3d:如何使用 OpenFolderPanel
- swift - 在 Swift 上层圆角
- algorithm - 以下哪个问题可以归结为哈密顿路径问题?
- azure-maps - 是否可以设置 Azure Maps 弹出关闭按钮的样式或创建自己的样式?
- tensorflow - 我应该使用独立的 Keras 库还是 tf.keras?
- java - 工具栏菜单显示,但在实现片段时不可点击
- c++ - 如何禁用调试控制台在我的代码中显示文件地址?
- jquery - 在移动网站上滚动时,汉堡下拉导航栏不可见
- bash - 从 MySQL 读取的嵌套 while 循环中的 IFS 的 BASH 问题