środa, 23 lipca 2014

Pointers to class members and their addresses

Today I was trying to help my work mate to design sort of unit testing helper. Basically it was about function pointers to virtual methods. Pointers to functions take virtuality into account when they are called with a reference or a pointer to the base class. The thing is that before I made my point, we have decided to check one simple thing.
So here's a puzzle for you. Given following code (and without using a compiler) try to answer following questions.

 1 #include <iostream>
 2 using namespace std;
 3 
 4 struct A {
 5     virtual void foo() {}
 6 };
 7 
 8 struct B : A {
 9     virtual void foo() {}
10 };
11 
12 int main(void) {
13     cout << hex << &A::foo << endl;
14     cout << hex << &B::foo << endl;
15 }

1. Will this code compile?
2. If yes, what will be the addresses of A::foo and B::foo methods, respectively?
3. If yes, what will be the output of this program?

Did you think about it? Remember... there's no hurry ;-).

Answer to the first question is positive as the code is well-formed C++. What about addresses of methods? C++ requires that every method has it's own unique place to live - an address (or offset). Virtuality does not change anything here, so we can assume that these addresses are some integers, let's say a and b.
The last question is the most tricky one. While intuition suggests that the output might look like following, it's not true at all.

  0x00c0d150
  0x00c0d15c

The real output of the program is:

  1
  1

What a surprise, right? The catch in above code fragment is that there's no overloaded function accepting function pointer for ostream. What the compiler can do instead? It can use implicit conversion. It turns out that there's implicit conversion from function pointer to a boolean. This would spill out if we used std::boolalpha flag, but who knew?

I'd like to ask you one more question: what if we used reinterpret_cast<void*> against member pointers in lines 13-14? Would the output have gotten right then?
Surprisingly, the code would not compile then. The reason for that are some machines that hold code and data memory in different locations. Therefore, it's hard to specify a portable way to convert between pointers to the data and pointers to the code.
The funny fact is that clang does not compile it, while GCC only issues a warning.


Brak komentarzy:

Prześlij komentarz