2

我试图了解用于实现指向非静态成员函数的指针的底层机制。我正在寻找一个类似于 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++ 编程语言”,

指向成员的指针...更像是结构的偏移量或数组的索引...

对于数据成员,我知道 Pointer-To-MemberFoo::j或多或少等同于offsetOf(Foo, j). 在我的主机环境中使用 gcc 编译器时的值是 4,它匹配的offsetOf(Foo, j)是 4。

对于函数成员,返回值为0x804844c. 这是属于全局数据区的一些地址(加载类的位置?)

所以我的问题是:

什么是 address 的“对象” 0x804844c

  1. 它不能是简单的offsetOf(),因为这是一个很大的偏移量。
  2. 它不能是 vtbl 的地址(或 vtbl 中条目的地址),因为我相信 vbtl 是与实例化对象相关联的实体,而不是与类相关联。
  3. 它不能是加载函数实现代码的地址。因为相同的指针在与派生对象一起应用时可以表现出多态性。

那么 address 处的对象是什么,当使用 operator or0x804844c时,它在将指向成员函数的指针转换为实际函数地址中的作用是什么?->*.*

4

1 回答 1

3

您不能使用 . 的%p转换说明符打印指向成员对象的值printf。它们不一定是常规指针。说明%p符需要一个void *值。严格来说,您甚至无法使用 便携式打印常规函数指针%p,因为这需要将函数指针转换为void *

指向成员的指针类型不一定适合一个可以保存地址的机器字。它们可以是具有多个字段的数据结构。

我相信 vbtl 是与实例化对象相关联的实体,而不是与类相关联。

这是不正确的。虚拟表是静态结构,实例只指向它们。给定类的所有实例共享一个指向同一个表的指针。

指向非静态成员函数的指针在一方面更简单,因为它不必处理类实例中任意数据成员的偏移量。但是,指向非静态成员函数的指针由于需要支持虚函数而变得复杂,并且通过这样的指针调用的代码不知道也不关心它是调用虚函数还是非虚函数.

指向成员函数的指针的一种实现策略涉及到 thunk:编译器生成的代码片段,它们执行正确的逻辑,以正确的方式进行调用。然后指向成员的指针可以指向 thunk。thunk 知道在this对象中的何处可以找到 vtable 指针,以及 vtable 中的哪个偏移量是最终要调用的函数。

于 2013-07-24T21:16:39.057 回答