2012-06-20 3 views
13

Это выглядит как std::cout не может напечатать адрес функции-члена, например:Как напечатать член адрес функции в C++

#include <iostream> 

using std::cout; 
using std::endl; 

class TestClass 
{ 
    void MyFunc(void); 

public: 
    void PrintMyFuncAddress(void); 
}; 

void TestClass::MyFunc(void) 
{ 
    return; 
} 

void TestClass::PrintMyFuncAddress(void) 
{ 
    printf("%p\n", &TestClass::MyFunc); 
    cout << &TestClass::MyFunc << endl; 
} 

int main(void) 
{ 
    TestClass a; 

    a.PrintMyFuncAddress(); 

    return EXIT_SUCCESS; 
} 

результат что-то вроде этого:

003111DB 
1 

Как я могу print MyFunc Адрес: std::cout?

ответ

16

Я не верю, что для этого существуют средства, предоставляемые языком. Есть перегрузки для operator << для потоков, чтобы распечатать нормальные void* указатели, но указатели функций функций не конвертируются в void* s. Это все специфичные для реализации, но обычно указатели на функции-члены реализуются как пара значений - флаг, указывающий, является ли функция-член виртуальной, и некоторые дополнительные данные. Если функция является не виртуальной функцией, эта дополнительная информация обычно является адресом фактической функции-члена. Если функция является виртуальной функцией, эта дополнительная информация, вероятно, содержит данные о том, как индексировать в таблицу виртуальных функций, чтобы найти функцию для вызова с учетом объекта-получателя.

В общем, я думаю, это означает, что невозможно распечатать адреса функций-членов, не вызывая неопределенное поведение. Для достижения этого эффекта вам, вероятно, придется использовать какой-то трюк, специфичный для компилятора.

Надеюсь, это поможет!

+0

Для некоторых интересных статистических данных о размерах и реализации указателей на член-функций в различных компиляторы, см. диаграмму внизу [этой статьи] (http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible). Однако, поскольку эта статья несколько датирована (написана в 2005 году), цифры скорее всего не точны, но они дают приблизительную идею. –

4

Один из способов сделать это (я не уверен, что это портативный):

void TestClass::PrintMyFuncAddress(void) 
{ 
    void (TestClass::* ptrtofn)() = &TestClass::MyFunc; 
    cout << (void*&)ptrtofn<< endl; 
} 

рабочий пример: http://ideone.com/1SmjW

+3

Эта программа демонстрирует очень мало. Этот стиль C-стиля представляет собой 'reinterpret_cast', который обычно просто реинтерпретирует память как тип назначения. В этом случае он будет использовать первые 'sizeof (void *)' байты, хранящиеся в указателе на член, как если бы это был 'void *', но если вы проверите размеры обоих типов, вы увидите, что они отличаются (указатель член больше). –

+0

@ DavidRodríguez-dribeas: Но он должен (вероятно) напечатать то же значение, что и «% p» printf(). Вероятно, они ошибаются (предполагается, что указатели элементов больше обычных указателей (обычно это верно)). –

13

Я хотел бы добавить другие ответы, что причина, что вы печатаете «1» вместо адреса, заключается в том, что по какой-то причине компилятор принуждает ваш указатель функции к логическому, так что вы действительно вызываете ostream& operator<< (bool val);

Это, кажется, не связано с функцией, являющейся член f соборование.

Вы можете раскрыть этот вид информации с лязгом ++ -cc1-дамп-АСТ:

(ImplicitCastExpr 0x3861dc0 <col:13, col:25> '_Bool' <MemberPointerToBoolean> 
    (UnaryOperator 0x3861940 <col:13, col:25> 'void (class TestClass::*)(void)' prefix '&' 
     (DeclRefExpr 0x38618d0 <col:14, col:25> 'void (void)' CXXMethod 0x3861500 'MyFunc' 'void (void)'))))) 
+0

Номера строк изменены. –

+1

спасибо за указание опции дампа синтаксиса. – fduff

+0

Добро пожаловать ... ;-) –

1

указатели на функции-члены нуждаются памяти тоже. Они также имеют размер. Так как о печати из памяти указателя:

template<typename R, typename T, typename... Args> 
std::string to_string(R (T::*func)(Args...)) 
{ 
    union PtrUnion 
    { 
     R(T::*f)(Args...); 
     std::array<unsigned char, sizeof(func)> buf; 
    }; 
    PtrUnion u; 
    u.f = func; 

    std::ostringstream os; 

    os << std::hex << std::setfill('0'); 
    for (auto c : u.buf) 
     os << std::setw(2) << (unsigned)c; 

    return os.str(); 
} 

Вы можете использовать его таким образом:

class TestClass 
{ 
    void foo(); 
}; 

... 

std::cout << to_string(&TestClass::foo) << std::endl;