我试图理解用于实现指向非静态成员函数的指针的底层机制。我正在寻找类似于vtbl(多态性虚拟表)如何在大图中工作的答案,而不必担心从编译器到编译器可能有所不同的细节。
示例:
#include <stdio.h>
class A {
public:
int i;
int j;
void foo(void) { };
};
int main ()
{
int A::*ptr_j = &A::j;
void (A::*ptr_f)(void) = &A::foo;
printf("Foo::j pointer to data member %p\r\n", ptr_j);
printf("Foo::foo pointer to function member %p\r\n", ptr_f);
}
结果是
的指针
Foo::j
指向数据成员0x4
的指针
Foo::foo
指向函数成员0x804844c
来自“Stroustrup的C ++编程语言”,
指向成员的指针...更像是结构的偏移量或数组的索引...
对于数据成员,我了解指针成员Foo::j
或多或少等同于offsetOf(Foo, j)
。在我的主机环境中使用gcc编译器时的值是4,它匹配offsetOf(Foo, j)
是4。
对于函数成员,返回值为0x804844c
。这是某个地址属于全局数据区(加载了哪个类?)
所以我的问题是:
什么是地址0x804844c
的“对象”。
offsetOf()
,因为这是一个很大的偏移。那么地址0x804844c
处的对象是什么,当运算符->*
或.*
为时,它将指针指向成员函数转换为实际函数地址的作用是什么?施加?
答案 0 :(得分:3)
您无法使用%p
printf
转换说明符打印指向成员对象的值。它们不一定是常规指针。 %p
说明符需要void *
值。严格来说,您甚至无法使用%p
打印常规函数指针,因为这需要将函数指针转换为void *
。
指针到成员类型不一定适合可以容纳地址的一个机器字。它们可以是包含多个字段的数据结构。
我相信vbtl是一个与实例化对象相关联的实体,而不是类。
这是不正确的。虚拟表是静态结构,实例仅指向它们。给定类的所有实例共享指向同一个表的指针。
指向非静态成员函数的指针在一个方面更简单,因为它不必处理类实例中任意数据成员的偏移量。但是,指向非静态成员函数的指针因支持虚函数的需要而变得复杂,并且通过这样的指针调用的代码不知道或不关心它是否调用了虚拟或非虚拟功能。
指向成员函数的指针的一个实现策略涉及thunks:编译器生成的代码片段,它们执行正确的逻辑以正确的方式进行调用。然后,指向成员的指针指向thunk。 thunk知道在this
对象中找到vtable指针的位置,以及vtable中哪个偏移量是最终要调用的函数。