2

我搜索了这篇文章: C++ : Getting function virtual 'address' with member function pointer

为了测试虚拟成员函数是否通常在对象的起始地址,我编写了如下代码:

#include <pwd.h>
#include <string.h>
#include <stdio.h>

class Base
{
public:
    int mBase1;
    char mBase2;
    virtual void foo()
    {
        fprintf(stderr,"Base foo called\n");
    }
};

class Child: public Base
{
public:
    int cc;
    virtual void foo()
    {
        fprintf(stderr,"Child foo called");
    }
};


int main(int argc, char *argv[])
{
    Base bb;
    fprintf(stderr,"[mBase1 %p][mBase2 %p] [foo %p]\n",&bb.mBase1,&bb.mBase2,&Base::foo);
    return 0;
}

编译时,我收到警告:

test.cpp:30:88: warning ‘%p’ expects argument of type ‘void*’, but argument 5 has type ‘void (Base::*)()’ [-Wformat]

输出是:

[mBase1 0xbfc2ca38][mBase2 0xbfc2ca3c] [foo 0x1]

我认为它是有线的。

  1. 是否有任何“漂亮的方法”来获取成员函数(不是静态成员函数地址?)。除了下面的方法,还有其他优雅的方法吗?

    typedef void (Base::*Foo)();
    Foo f = &Base::foo();
    
  2. 为什么没有&Base::foo得到正确的 C++ 成员地址?

4

3 回答 3

5
于 2013-02-16T13:15:27.130 回答
1

如前所述,指向成员的指针不是指针,它们转换为 int 或 void * 等类型通常是没有意义的。特别是 0x1 最有可能表示对象 vtable 中的第一个或第二个条目。让我们考虑这段代码:

void (*Base::*test)() = &Base::foo
Child f;
f.*test();

对于第三行,编译器获取 f 的地址。将其转换为 Base 对象的地址,在本例中为 NOOP)从该地址读取 f 的 vtable 的地址(vtable 通常是对象中的第一个数据项),然后添加两个该地址 1* sizeof(void*) 并在该地址调用函数,这在很大程度上等同于这个 C :

typedef void (*FnPtr)();
FnPtr *vtable=*(FnPtr **)&(Base &)f;
(*(vtable[(int)test]))(); or (*(vtable[(int)test - 1]))();

这只是对于您拥有的编译器的确切版本可能正确的假设。即使对未来的编译器版本也不能保证可移植性。

于 2017-03-30T04:20:16.483 回答
0

为什么 &Base::foo 没有得到正确的 C++ 成员地址?

Base::foo 不是静态方法。没有属于 Base 的 foo。我想知道 [foo 0x1] 是什么意思(例如:如果整个事情没有多大意义,为什么 0x1 )?

于 2013-02-16T15:44:56.153 回答