Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this the same even when object pointers differ in multiple inheritance?

When using multiple inheritance C++ has to maintain several vtables which leads to having "several views" of common base classes.

Here's a code snippet:

#include "stdafx.h"
#include <Windows.h>

void dumpPointer( void* pointer )
{
    __int64 thisPointer = reinterpret_cast<__int64>( pointer );
    char buffer[100];
   _i64toa( thisPointer, buffer, 10 );
    OutputDebugStringA( buffer );
    OutputDebugStringA( "\n" );
}

class ICommonBase {
public:
    virtual void Common() = 0 {}
};

class IDerived1 : public ICommonBase {
};

class IDerived2 : public ICommonBase {
};

class CClass : public IDerived1, public IDerived2 {
public:
    virtual void Common() {
        dumpPointer( this );
    }
    int stuff;
};

int _tmain(int argc, _TCHAR* argv[])
{
    CClass* object = new CClass();
    object->Common();
    ICommonBase* casted1 = static_cast<ICommonBase*>( static_cast<IDerived1*>( object ) );
    casted1->Common();
    dumpPointer( casted1 );

    ICommonBase* casted2 = static_cast<ICommonBase*>( static_cast<IDerived2*>( object ) );
    casted2->Common();
    dumpPointer( casted2 );

    return 0;
}

it produces the following output:

206968 //CClass::Common this
206968 //(ICommonBase)IDerived1::Common this
206968 //(ICommonBase)IDerived1* casted1
206968 //(ICommonBase)IDerived2::Common this
206972 //(ICommonBase)IDerived2* casted2

here casted1 and casted2 have different values which is reasonable since they point to different subobjects. At the point when the virtual function is called the cast to the base class has been done and the compiler doesn't know that it was a most derived class originally. Still this is the same each time. How does it happen?

like image 316
sharptooth Avatar asked Jul 27 '26 17:07

sharptooth


1 Answers

When multiple inheritance is used in a virtual function call, the call to the virtual function will often go to a 'thunk' that adjusts the this pointer. In your example, the casted1 pointer's vtbl entry doesn't need a thunk becuase the IDerived1 sub-object of the CClass happens to coincide with the start of the CClass object (which is why the casted1 pointer value is the same as the CClass object pointer).

However, the casted2 pointer to the IDerived2 sub-object doesn't coincide with the start of the CClass object, so the vtbl function pointer actually points to a thunk instead of directly to the CClass::Common() function. The thunk adjusts the this pointer to point to the actual CClass object then jumps to the CClass::Common() function. So it will always get a pointer to the start of the CClass object, regardless of which type of sub-object pointer it might have been called from.

There's a very good explanation of this in Stanley Lippman's "Inside the C++ Object Model" book, section 4.2 "Virtual Member Functions/Virtual Functions Under MI".

like image 69
Michael Burr Avatar answered Jul 30 '26 07:07

Michael Burr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!