I have been reading around how dynamic_cast
works and from what I gathered so far, it obtains the object, gets the vptr, goes to the vtable and in the -1 or 0th element, there is a pointer to a type_info
object. However, from this point on it gets a little hazy:
Does the type_info
object contain all data necessary to (possibly) do the cast, or does the runtime system need to access other type_info objects?
I am trying to understand how many different objects/vtables and type_info objects are accessed whilst checking an inheritance hierarchy during dynamic_cast
.
EDIT: Compiler-wise MSVC or GCC
It's unspecified, and depends on the implementation, but the
information relative to converting a pointer to another type in
the hierarchy, at least when virtual inheritance is involved,
depend on the actual most derived class; having found that the
class is a D
, information from B
or C
would not help
finding the actual address of A
in the D
. The position of
the A
part in the object will differ depending on the most
derived class. To see this clearly, create classes with a data
member, and display the different addresses:
struct B
{
int b;
B() : b( 1 ) {}
virtual ~B() = default;
};
struct L : virtual public B
{
int l;
L() : l( 2 ) {}
};
struct R : virtual public B
{
int r;
R() : r( 3 ) {}
};
struct D: public L, public R
{
int d;
D() : d( 4 ) {}
};
struct E : public D
{
int e;
E() : e( 5 ) {}
};
template <typename T>
class HexDump
{
T const& myObj;
public:
HexDump( T const& obj ) : myObj( obj ) {}
friend std::ostream& operator<<( std::ostream& dest, HexDump const& obj )
{
dest.fill( '0' );
dest.setf( std::ios_base::hex, std::ios_base::basefield );
uint32_t const* p = reinterpret_cast<uint32_t const*>( &obj.myObj );
for ( int i = 0; i < sizeof(T) / sizeof(uint32_t); ++ i ) {
if ( i != 0 ) {
dest << ' ';
}
dest << std::setw( sizeof(uint32_t) * 2 ) << p[i];
}
return dest;
}
};
template <typename T>
HexDump<T>
hexDump( T const& obj )
{
return HexDump<T>( obj );
}
int
addrDiff( void const* lhs, void const* rhs )
{
return static_cast<char const*>( lhs ) - static_cast<char const*>( rhs );
}
int
main()
{
B* aD = new D;
B* anE = new E;
std::cout << "position of B in D: " << addrDiff( aD, dynamic_cast<D*>( aD ) ) << std::endl;
std::cout << "position of B in E: " << addrDiff( anE, dynamic_cast<E*>( anE ) ) << std::endl;
std::cout << "D: " << hexDump( *dynamic_cast<D*>( aD ) ) << std::endl;
std::cout << "E: " << hexDump( *dynamic_cast<E*>( anE ) ) << std::endl;
return 0;
}
The initialization of the various data elements allow you to see the position of the different classes (or their payloads) clearly.
Note in particular that the vptr of the B
sub-object is different in a D
and in a B
; the RTTI infos in the vtbl it points to must reflect the most derived class.