2013-07-24 81 views
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

选自“C++编程语言通过Stroustrup的”,

甲指针构件...更像是一个偏移量的结构或索引到一个数组...

对于数据成员,我知道指针对成员Foo::j或多或少等于offsetOf(Foo, j)。在我的主机环境中使用gcc编译器时的值为4,它与offsetOf(Foo, j)的值为4.

对于函数成员,返回值为0x804844c。这是一些地址属于全局数据区

所以我的问题是(其中的类加载):

什么是“对象”,也就是地址0x804844c

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

那么什么是在地址0x804844c对象,什么时候应用操作->*.*其在翻译的指针到成员函数到实际的函数地址的作用是?

+2

在C++语言标准使用该术语的意义上,函数不是“对象”。请注意,成员函数通常作为具有附加参数的常规函数​​来实现(该对象变成'this')。在那些实现中,它们不驻留在类实例本身中,而是与代码段中的其他(免费)函数一起。 – dyp

+0

*“它不能是加载函数的实现代码的地址,因为当应用派生对象时,同一个指针可能会呈现多态。” [这个答案](http://stackoverflow.com/a/1087671/420683) – dyp

回答

3

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

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

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

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

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

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