its going to look a lot like it would if you wrote it in C. Machine language, or assembly language, does not really do objects. So you are talking a mashup of C-like structs and C-like pointers that are strung together to represent the higher level construct.
Casting is all but ignored at the machine level. At that point, it already resolved what it is and what is to be done with it. Its a pile of bytes, the compiled code takes those bytes and does things to them, could be string things, could be integers, could be doubles... whatever. The code is sort of hard-coded at this level to do what needs doing to the bytes.
Without virtual inheritance, things are quite simple: the object representation is typically the same across implementations; the derived class object contains anonymous base class sub-objects (one for each base class).
Except for the one base class (which has its sub-object is at an offset of zero bytes), pointer conversions involve a small run-time overhead: check for null pointer, and if not null add or subtract an offset (this offset is a constant known at compile time).
#include <cstdint>
#include <cstddef>
struct A { int a = 0 ; };
struct B { double b = 0 ; };
struct C : A, B { short c = 0 ; };
// convert pointer to derived classs to pointer to base class
// if the pointer is null, the result of the conversion is nullptr
// otherwise, adjust with an offset to get the valid pointer to the
// base class subobject (in this example, the offset is 8 bytes)
const B* pb( const C* pc ) { return pc ; }
const B* pb1( const C* pc )
{
if( pc == nullptr ) returnnullptr ; // if pc is null, return null
// otherwise, add an offset of 8
auto ptr_val = reinterpret_cast<std::uintptr_t>(pc) ;
returnreinterpret_cast< const B* >( ptr_val + 8 ) ;
}
//////////////////////////////////////////////////////////////////////////
// convert pointer to base classs to pointer to derived class via a cast
// if the pointer is null, the result of the conversion is nullptr
// otherwise, adjust with an offset to get the valid pointer to the
// derived class object which contains this base class subobject
// (in this example, the offset is -8 bytes)
const C* pc( const B* pb ) { returnstatic_cast<const C*>(pb) ; }
const C* pc1( const B* pb )
{
if( pb == nullptr ) returnnullptr ;
auto ptr_val = reinterpret_cast<std::uintptr_t>(pb) ;
returnreinterpret_cast< const C* >( ptr_val - 8 ) ;
}
We can see that the same code is generated for functions pb and pb1.
The code generated for functions pc and pc1 are also identical. https://gcc.godbolt.org/z/kBmHX2
Where virtual inheritance is involved, things are considerably more complicated, and there are differences between implementations. Though somewhat dated, this book gives an excellent idea of what goes on under the hood:
'Inside the C++ Object Model' by Lippman https://www.amazon.com/Inside-Object-Model-Stanley-Lippman/dp/0201834545
Thanks for the examples, they were very useful. Also the reference to the book will help me cause it was difficult to find bibliography related to the subject.