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.
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):
This first argument is then available via the
thispointer.now,
Afuncin the example above internally has the signaturevoid Afunc(A* const this)whileCFunchas the internal signaturevoid 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:
This code is treated internally similar to the following (pseudo-code):
This cast is, for single-inheritance, a no-operation (i.e. it can just be removed) through the trick mentioned in the quote: the
Cobject and its parentAobject are stored at the same physical address. An object of typeCwhich is stored at an addressxin memory is physically laid out in such a way that its parent object of typeAis also stored at the addressx, and is followed by all other members thatCmay have (but in your case it has no members, andsizeof(C) == sizeof(A)).