我在以下链接中找到了这种技术:http: //www.codeproject.com/Tips/90875/Displaying-vtable-when-debugging
在那里,他使用了一个辅助变量
void (**vt)() = *(void (***)())ptr;
帮助显示虚函数表。
但是,如果我将其更改为
void (**vt)() = (void (**)())ptr;
它不像前一个那样工作。
有人可以帮我解释一下这里有什么魔法吗?
我在以下链接中找到了这种技术:http: //www.codeproject.com/Tips/90875/Displaying-vtable-when-debugging
在那里,他使用了一个辅助变量
void (**vt)() = *(void (***)())ptr;
帮助显示虚函数表。
但是,如果我将其更改为
void (**vt)() = (void (**)())ptr;
它不像前一个那样工作。
有人可以帮我解释一下这里有什么魔法吗?
为了清楚起见,让我们引入一个 typedef。
typedef void (**thing)();
那么第一个代码是
thing vt = *(thing*) ptr;
第二个
thing vt = (thing) ptr;
也就是说,第一个说“将 ptr 视为一个事物的指针,并给我它指向的内容”,第二个说“将 ptr 视为一个事物”。
区别应该很明显。
它完全起作用的原因是针对 Visual C++ 编译器和 Visual Studio 调试器的。
如果调试器不知道对象到底是什么类型,它会显示它所知道的,即它是基类的一个实例,它只知道基类的 vtable 有多少条目。
(您在这里遗漏了一个关键部分,即向监视窗口中的项目添加预期数量的表条目。这使得调试器将该内存显示为包含您所说的尽可能多的元素的数组。)
所以诀窍在于为 vtable 命名,并告诉调试器要从表中显示多少元素。
许多 C++ 实现中的虚拟表指针是sizeof(void (**)())
对象的第一个字节。当您取消引用该指针时,您将获得真实虚拟表的起始地址。这就是工作代码的含义。
该cdecl
程序可能在这里有点帮助:
cdecl> explain void (***foo)()
declare foo as pointer to pointer to pointer to function returning void
cdecl> explain void (**foo)()
declare foo as pointer to pointer to function returning void
第一个代码将指向您的对象的指针转换为可正确解引用的指针(指向函数指针的指针)void (***)()
,然后解引用它以获取虚拟表的起始地址,该地址属于类型void (**)()
(指向函数的指针),它指向类型为void (*[])()
(指向函数的指针数组)的虚拟表的开头。
第二个只是将指向对象的指针转换为指向返回 void 的函数的指针;存储在变量 vt 中的地址就是你的对象的地址。
class Object {
public:
virtual void foo() {};
};
Object x;
// is approximately equivalent to
struct Object {
void (**_vtable)();
};
void _Object_foo(Object this) {
}
// this does not really compile, however,
// because a pointer mismatch
void (*_vtable_for_Object[])() = {
_Object_foo
};
Object y;
y._vtable = _vtable_for_Object;
因此通过拥有
Object *ptr = new Object();
// x now points to a block of memory,
// whose first bytes are void (**_vtable)()
// then
void (**vt)() = *(void (***)())ptr;
// is equivalent in our analogy to
void (**vt)() = ptr->_vtable;
// except that the C++ does not allow you to
// access the virtual table directly without
// the trickery of the former