Question

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

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top