2012-06-20 34 views
13

Dường như std::cout không thể in địa chỉ hàm thành viên của, ví dụ:Làm thế nào để in địa chỉ hàm thành viên trong 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; 
} 

kết quả là một cái gì đó như thế này:

003111DB 
1 

Làm thế nào tôi có thể In MyFunc 's địa chỉ bằng cách sử dụng std::cout?

Trả lời

16

Tôi không tin rằng có bất kỳ cơ sở nào được cung cấp bởi ngôn ngữ để thực hiện việc này. Có quá tải cho operator << cho các luồng để in ra các con trỏ bình thường void*, nhưng các con trỏ hàm thành viên không thể chuyển đổi thành void* s. Đây là tất cả các chi tiết cụ thể cho việc triển khai thực hiện, nhưng các con trỏ hàm thành viên thường được thực hiện như một cặp giá trị - một lá cờ cho biết chức năng thành viên có phải là ảo hay không và một số dữ liệu bổ sung. Nếu hàm là một hàm không phải ảo, thông tin bổ sung đó thường là địa chỉ của hàm thành viên thực tế. Nếu hàm là một hàm ảo, thông tin bổ sung đó có thể chứa dữ liệu về cách lập chỉ mục vào bảng chức năng ảo để tìm hàm để gọi cho đối tượng người nhận.

Nói chung, tôi nghĩ điều này có nghĩa là không thể in ra địa chỉ của các hàm thành viên mà không cần gọi hành vi không xác định. Bạn có thể phải sử dụng một số thủ thuật cụ thể của trình biên dịch để đạt được hiệu ứng này.

Hy vọng điều này sẽ hữu ích!

+0

Đối với một số thống kê thú vị về kích cỡ và triển khai của con trỏ-to-viên-chức năng trong khác nhau trình biên dịch, xem biểu đồ gần cuối [bài viết này] (http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible). Tuy nhiên, kể từ khi bài viết đó có phần ngày tháng (viết năm 2005), các con số có thể không còn chính xác nữa, nhưng chúng đưa ra một ý tưởng thô. –

4

Một cách để làm điều đó là (Tôi không chắc nó cầm tay):

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

dụ làm việc: http://ideone.com/1SmjW

+3

Chương trình đó thể hiện rất ít. Đó là cast kiểu C là một 'reinterpret_cast' mà thường chỉ diễn giải lại bộ nhớ như là kiểu đích. Trong trường hợp này, nó sẽ sử dụng 'byte sizeof (void *)' đầu tiên được lưu trữ trong con trỏ tới thành viên như thể nó là một 'void *', nhưng nếu bạn kiểm tra kích thước của cả hai loại, bạn sẽ thấy chúng khác nhau (con trỏ thành viên lớn hơn). –

+0

@ DavidRodríguez-dribeas: Nhưng nó nên (có lẽ) in cùng giá trị với "% p" của printf(). Cả hai đều có thể sai (giả sử con trỏ thành viên lớn hơn con trỏ bình thường (thường là đúng)). –

13

Tôi muốn thêm vào câu trả lời khác, rằng lý do mà bạn đang nhận được '1' in thay vì một địa chỉ, đó là, vì một lý do nào đó, trình biên dịch là ép con trỏ hàm của bạn vào boolean, để bạn thực sự gọi ostream& operator<< (bool val);

Điều này dường như không liên quan đến hàm một thành viên f unction.

Bạn có thể khám phá loại thông tin này với kêu vang ++ -cc1 -ast-dump:

(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

Số dòng được thay đổi. –

+1

cảm ơn bạn đã chỉ ra tùy chọn kết xuất cú pháp. – fduff

+0

Bạn được chào đón ... ;-) –

1

Con trỏ tới hàm thành viên cần nhớ, quá. Họ cũng có một kích thước. Vậy làm thế nào về việc in ra ký ức của con trỏ:

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(); 
} 

Bạn có thể sử dụng nó theo cách này:

class TestClass 
{ 
    void foo(); 
}; 

... 

std::cout << to_string(&TestClass::foo) << std::endl; 
Các vấn đề liên quan