Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function Pointers

I was going through an article by Don Clugston on Codeproject. It is a beautiful article and is quite famous. In the following code snippet, I found a particular notion very difficult to comprehend:

class A {
 public:
       virtual int Afunc() { return 2; };
};

class B {
 public: 
      int Bfunc() { return 3; };
};

// C is a single inheritance class, derives only from A
class C: public A {
 public: 
     int Cfunc() { return 4; };
};

// D uses multiple inheritance
class D: public A, public B {
 public: 
    int Dfunc() { return 5; };
};

The snippet is followed by the following paragraph:

Suppose we create a member function pointer for class C. In this example, Afunc and Cfunc are both member functions of C, so our member function pointer is allowed to point to Afunc or Cfunc. But Afunc needs a this pointer that points to C::A (which I'll call Athis), while Cfunc needs a this pointer that points to C (which I'll call Cthis). Compiler writers deal with this situation by a trick: they ensure that A is physically stored at the start of C. This means that Athis == Cthis. We only have one this to worry about, and all's well with the world.

The one and the only thing I want to understand is the line in BOLD and ITALICS in the above paragraph.

I do not fully comprehend Afunc needs a pointer to C::A while Cfunc needs a pointer to C is quite natural.

Any help would be appreciated.

like image 255
msiyer Avatar asked Jun 12 '12 09:06

msiyer


Video Answer


1 Answers

By calling a member function in C++, what internally happens is that the instance is passed as a hidden first argument (note that this behaviour is strictly implementation-defined behaviour, though. The C++ standard has nothing to say on this topic, it’s just a very common way of implementing it):

x.f(y); // is treated internally as:
f(&x, y);

This first argument is then available via the this pointer.

now, Afunc in the example above internally has the signature void Afunc(A* const this) while CFunc has the internal signature void CFunc(C* const this).

Note that the argument types in both cases are different, so when you call the functions on the same object, a different pointer has to be passed. C++ solves this by defining an implicit conversion from any derived object to its base object. That is, in the following code:

C* pc = something;
pc->Afunc();

This code is treated internally similar to the following (pseudo-code):

C* pc = something;
Afunc(static_cast<A*>(pc));

This cast is, for single-inheritance, a no-operation (i.e. it can just be removed) through the trick mentioned in the quote: the C object and its parent A object are stored at the same physical address. An object of type C which is stored at an address x in memory is physically laid out in such a way that its parent object of type A is also stored at the address x, and is followed by all other members that C may have (but in your case it has no members, and sizeof(C) == sizeof(A)).

like image 62
Konrad Rudolph Avatar answered Sep 25 '22 04:09

Konrad Rudolph