dynamic_cast requires virtual functions (polymorphic), why?


First of all, if you ALREADY know the cast is safe, static_cast can replace dynamic_cast, even in mutilple inheritance, except virtual inheritance.
Please see this post for why.

struct D: virtual B {...};//have to use dynamic_cast to cast B* to D*

I have to go through a few steps to explain this.
1. dynamic_cast needs access to RTTI (again, the above link says why).
2. RTTI is only accessable through vtable pointer.
3. no virtual functions, no vtable pointer. (RTTI can sit right above vtable)
4. Conclusion, dynamic_cast needs virtual function.

Advertisements

When is dynamic_cast the ONLY way that works?


If we know the cast is safe, we can use static_cast for downcasting in almost all cases.
Have a look at this example:

struct CMultiBase1
{
	int mbase1;
	virtual void f() {	};
};
struct CMultiBase2
{
	int mbase2;
	virtual void f() {	};
};
struct CMultiSub: CMultiBase1, CMultiBase2
{
};
void TestMulti()
{
	CMultiSub s;
	CMultiSub *pFrom1Static, *pFrom1Dynamic, *pFrom2Static, *pFrom2Dynamic;
	CMultiBase2 *p2 = &s;
	CMultiBase1 *p1 = &s;
	pFrom1Static = static_cast<CMultiSub *>(p1); 
	pFrom1Dynamic = dynamic_cast<CMultiSub *>(p1);
	pFrom2Static = static_cast<CMultiSub *>(p2); 
	pFrom2Dynamic = dynamic_cast<CMultiSub *>(p2);
	_ASSERT(pFrom1Static == pFrom1Dynamic && pFrom2Static == pFrom2Dynamic
		&& pFrom1Static == &s && pFrom2Static == &s);
}

No matter how we downward cast them, we always get &s, and therefore static_cast can be used for dynamic_cast.
However, in the following snippet, dynamic_cast is the only way that works:

struct CMultiBase
{
	int mbase;
	virtual void f() {	};
};
struct CMultiBase1: virtual CMultiBase
{
	int mbase1;
};
void TestVirtualSingleInheritance()
{
	CMultiBase1 b1;
	b1.mbase1 = 2;
	CMultiBase *pb = &b1;
	CMultiBase1 *pb1 = dynamic_cast<CMultiBase1 *>(pb); //no other cast works here
	_ASSERT(pb1->mbase1 == 2); //dynamic_cast is good
	_ASSERT(reinterpret_cast<void**>(pb1) + 2 == reinterpret_cast<void*>(pb));
	//pb1 = static_cast<CMultiBase1 *>(pb); //compiling error
	pb1 = reinterpret_cast<CMultiBase1 *>(pb);
	_ASSERT(pb1->mbase1 != 2); //reinterpret_cast casts to the wrong pointer
}

It’s impossible to cast from virtual base pointer to derived pointer without RTTI. dynamic_cast is the only one that can use RTTI. Therefore, dynamic_cast is the only way here.

What does virtual inheritance mean?


It’s well known that virtual inheritance is to solve the diamond problem.
Here I dig a little deeper, with only two classes.
struct B: virtual A {…}; What difference is ‘virtual’ making here?

struct CMultiBase
{
	int mbase;
};
struct CMultiBase1: virtual CMultiBase
{
	int mbase1;
};
void TestVirtualSingleInheritance()
{
	CMultiBase1 b1;
	CMultiBase *pb = &b1;
	CMultiBase1 *pb1 = &b1;
	std::cout << std::hex << pb1 << " " << &pb->mbase;
	_ASSERT(reinterpret_cast<void**>(pb1) + 2 == reinterpret_cast<void*>(&pb->mbase));
	// this assertion fails if no 'virtual'. Adding 2 to skip vbptr and mbase1
}

Without ‘virtual’, mbase is the the first member in CMultiBase1, and therefore pb1 == &pb->mbase, and the _ASSERT fails.
With ‘virtual’, vbptr, instead of mbase, becomes the first member in CMultiBase1, and therefore the _ASSERT succeeds.
To put it simply, ‘virtual’ moves the base class away from the beginning of the derived class, and vbptr tells where the base class goes.

How many vtable pointers in one derived object in single virtual inheritance?


How many vtable pointers in b1 (Visual studio 2010)?
The answer is 2 if CMultiBase1 add virtual functions or 1 if CMultiBase1 add no virtual functions.

enum { EBASE1, EBASE2, EBASE3, EBASE4, EBASE5 };
struct CMultiBase
{
	int mbase;
	virtual int f()
	{
		return EBASE1;
	};
};
struct CMultiBase1: virtual CMultiBase
{
	int f()
	{
		return EBASE2;
	};
	virtual int f1()
	{
		return EBASE3;
	};
};
void TestVirtualSingleInheritance()
{
	CMultiBase1 b1; //we have only one object
	CMultiBase *pb = &b1;
	CMultiBase1 *pb1 = &b1;
	unsigned int * pRawDword = reinterpret_cast(&b1); //points to the first DWORD of our project
	_ASSERT(sizeof(unsigned int) == 4);
	std::cout << std::hex << pb << " " << pb1;
	_ASSERT(reinterpret_cast(pb) != reinterpret_cast(pb1));
}

(pRawDword + 0) points to the newly added part of CMultiBase1’s vtable,
because *(int*)(*(pRawDword +0)) shows CMultiBase1::f1().
(pRawDword + 2) points to the part of CMultiBase1’s vtable that was originally from CMultiBase,
because *(int*)(*(pRawDword +2)) shows CMultiBase ::f ()
Therefore, there are two vtable pointers in b1, pRawDword+2 and pRawDword+0
Here is how to see virtual function addresses:
From the watch window in Visual Studio, expand pb, expand __vfptr.
It can also be seen that if CMultiBase1 doesn’t introduce any new virtual functions(take out f1), there is only one vtable pointer, and (pRawDword + 0) instead points to vbptr(virtual base table pointer).