C++ 有没有办法得到C++;虚拟成员函数地址

C++ 有没有办法得到C++;虚拟成员函数地址,c++,function,virtual,C++,Function,Virtual,我搜索了这篇文章: 为了测试虚拟成员函数是否通常位于对象的起始地址,我编写了以下代码: #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");

我搜索了这篇文章:

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

#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;
}
输出为:

[mBase1 0xbfc2ca38][mBase2 0xbfc2ca3c] [foo 0x1]
我认为它是有线的。
  • 是否有任何“漂亮的方法”来获取成员函数(不是静态成员函数地址?)。除了下面的方法,还有其他优雅的方法吗

    typedef void (Base::*Foo)();
    Foo f = &Base::foo();
    
  • < > >为什么<代码>和BAS::FoO < /代码>获得正确的C++成员地址?< /P> 为什么不&基基::FO得到正确的C++成员地址?< /P> 指向成员的指针与指针的类型不同(事实上,它们的偏移量比类型本身多),特别是,它们不能转换为
    void*
    (它们可以在GCC上转换,但可能是编译器扩展)

    见C++11标准第8.3.3/3段:

    指向构件的指针不得指向**类(9.4)的静态构件、引用类型的构件或“cv void” [Note:参见5.3和5.5。<强>类型“指向成员的指针”与“指针”类型< /强>不同,即指向成员的指针仅由指向成员声明符语法的指针声明,而不是由指针声明语法。C++中没有“引用成员”类型。-EntNote

    此外,第5.2.10/10段(关于
    重新解释
    )定义了指向成员的指针的唯一可能转换,如下所示:

    如果T1和T2都是函数类型或对象类型,则类型为“指向T1类型的X的成员的指针”的prvalue可以显式转换为不同类型的prvalue“指向T2类型的Y的成员的指针”。空成员指针值(4.11)转换为目标类型的空成员指针值。此转换的结果未指定,以下情况除外:

    -将“指向成员函数的指针”类型的prvalue转换为指向成员函数类型的不同指针并返回其原始类型将生成指向成员值的原始指针

    -将类型为“指向T1类型X的数据成员指针”的PR值转换为类型为“指向T2类型Y的数据成员指针”(其中T2的对齐要求不比T1严格)并返回到其原始类型会产生指向成员的原始指针值

    为什么不&基基::FO得到正确的C++成员地址?< /P>
    Base::foo不是静态方法。没有属于基地的食物。我想知道[foo 0x1]是什么意思(例如:如果整个事情没有多大意义,为什么要使用0x1)?

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

    void (*Base::*test)() = &Base::foo
    Child f;
    f.*test();
    
    对于第三行,编译器获取f的地址。将其转换为基本对象的地址(在本例中为NOOP)从该地址读取vtable for f的地址(vtable通常是对象中的第一个数据项),然后添加两个地址为1*sizeof(void*)的函数,并在该地址处调用该函数,该函数基本上相当于此C:

    typedef void (*FnPtr)();
    FnPtr *vtable=*(FnPtr **)&(Base &)f;
    (*(vtable[(int)test]))(); or (*(vtable[(int)test - 1]))();
    
    对于您拥有的编译器的确切版本,这是唯一可能成立的假设。即使在将来的编译器版本中也不能保证可移植性。

    函数本身不会位于对象的开头;指向v表的指针(通常)位于对象的开头,在指向的表中可以找到指向函数的指针(但不是函数本身)。“普通”指针,包括函数指针,通常表示为机器地址;成员指针不是。认为<代码>派生D;(d.*f)()可以工作,但是调用
    Derived::Foo
    ,正如人们所期望的那样。如果
    f
    是一个特定的地址,您就不能这样做。
    typedef void (*FnPtr)();
    FnPtr *vtable=*(FnPtr **)&(Base &)f;
    (*(vtable[(int)test]))(); or (*(vtable[(int)test - 1]))();