回 帖 发 新 帖 刷新版面

主题:[讨论]类成员函数地址的问题

#include <iostream>
using namespace std;
class Object
{
    public:
        void f(){
            cout << "Object::f()";
        }

        void f(int){
            cout << "Object::f(int)";
        }
};

int main()
{
    cout << "Object::f :"<< &Object::f<<endl;
}
这个代码编译会出错,那么该如何获得 Object::f()和Object::f(int)的地址呢?

回复列表 (共3个回复)

沙发


[code=c]
#include <iostream>
using namespace std;
class Object
{
public:
    void f(){
        cout << "Object::f()";
    }

    void f(int){
        cout << "Object::f(int)";
    }
};

int main()
{
    void (Object::*p)();//用两个指向成员函数的指针
    void (Object::*q)(int);
    q=&Object::f;
    p=&Object::f;
    cout << "Object::f :"<< p<<endl;
    cout << "Object::f(int):"<<q<<endl;//断点设在这一行 
    }

[/code]

执行到断点后 监视器中:
名称        值            类型
Object::f(void)    0x00161600 Object::f(void)    void (void)
Object::f(int)    0x00161650 Object::f(int)    void (int)
*p               0x00161262 Object::f(void)    void (void)
*q        0x00161271 Object::f(int)    void (int)
p        0x00161262 Object::f(void)    void (void)*
q        0x00161271 Object::f(int)    void (int)*
看了半天我也挺糊涂的  请高手解答

板凳

“指向非static的成员函数的指针”(为了方便起见,下面简称“成员函数指针”),这个话题有点复杂。

“成员函数指针”虽然也属于指针,但它跟一般的指针差距可不小。
首先,C语言的所有类型的指针(甚至包括指向函数的指针),都可以隐式转化为void指针。但是“成员函数指针”则不能这样转化,否则编译器报错。
其次,32位系统中,一般指针的大小都是4字节,但成员函数指针则不一定。可能有朋友不太相信,让我们眼见为实:[quote]class T1
{
};

class T2A
{
};

class T2B
{
};

class T2 : public T2A, public T2B
{
};

class T3A
{
};

class T3 : virtual public T3A
{
};

class T4;

#include <stdio.h>

int main()
{
    printf("%d\n", sizeof(void(T1::*)()));
    printf("%d\n", sizeof(void(T2::*)()));
    printf("%d\n", sizeof(void(T3::*)()));
    printf("%d\n", sizeof(void(T4::*)()));
    return 0;
}[/quote]
运行环境Visual C++ Express 2008,采用默认的编译选项,输出结果为4, 8, 12, 16。
实验发现,在VC系列的编译器中,采用默认的编译选项,一个类在不继承(或者单继承)、多继承、virtual继承、未定义这四种情况下,其“成员函数指针”的大小分别是4, 8, 12, 16字节。
根据网上其他人的测试数据,有的编译器在有的情况下,“成员函数指针”甚至可以占用20个字节。

就楼主的问题,希望取得成员函数的地址,并输出。这个要求看起来很简单,但我也没有什么好的解决办法。如何输出一个占用16个字节的值呢?除了采用一些不可移植的手段,我真的想不出其它办法来。
即使可以确保所有的“成员函数指针”都是4个字节,也没有什么好的办法可以顺利的输出正确的函数地址。对于一般的成员函数,确实可以用这4个字节保存函数代码所在的内存地址,但是对于virtual成员函数,则并不是这样——virtual函数需要根据对象的实际类型确定最终应该调用哪个函数,此时对象都还没有指定,怎么可能得到正确的函数地址呢?

我把1楼alweeq86的代码稍作修改,得到如下代码:
[quote]#include <iostream>
using namespace std;

class A
{
};

class B
{
};

class Object
{
public:
    virtual void f(){
        cout << "Object::f()";
    }

    void f(int){
        cout << "Object::f(int)";
    }
};

int main()
{
    void (Object::*p)();//用两个指向成员函数的指针
    void (Object::*q)(int);
    p = &Object::f;
    q = &Object::f;
    printf("%p\n", p);
    printf("%p\n", q);

    Object o;
    o.f();
    o.f(10);
}[/quote]

运行时输出
012D1267
012D1226

查看汇编代码为:
    o.f();
0011157A  lea         ecx,[o] 
0011157D  call        Object::f (1110F5h) 
    o.f(10);
00111582  push        0Ah  
00111584  lea         ecx,[o] 
00111587  call        Object::f (111226h) 

可见,非virtual的那个函数(带int的版本)的地址是正确的,都是0x00111226。而virtual的那个函数地址是错误的,输出的是0x012D1267,但实际上却是0x001110F5。

3 楼


高手  向你致敬!

[b]一个类在不继承(或者单继承)、多继承、virtual继承、未定义这四种情况下,其“成员函数指针”的大小分别是4, 8, 12, 16字节。[/b]

这个还真不知道,有空我试一下。

我来回复

您尚未登录,请登录后再回复。点此登录或注册