Discussion:
Q) Identifying the correct data type at run time (RTTI and dynamic_cast)
(too old to reply)
Generic Usenet Account
2007-12-12 20:40:09 UTC
Permalink
I ran a small experiment involving RTTI and dynamic casting.
Basically what I did was to cast a base class pointer to a derived
type (yes, I know that is not kosher). I then performed
dynamic_casting and I invoked RTTI. In both instances, the run time
environment did not pick up the fact that what I was claiming to be a
derived pointer was in fact the base pointer. However, when I invoked
a virtual method using the ostensibly derived class pointer (which was
in reality the base class pointer), it was the base class method that
got invoked (and correctly so). Is this the correct behavior, or is
it the quirk of my compiler (gcc version 3.3.1)?

I know that I am a nobody, but here is what I was expecting:
dynamic_casting: Since the pointer was really to the base class, and
not to the derived class, as claimed, dynamic_casting should have
picked that up and returned NULL.

RTTI: The typeid for the pointer should have been that of the base,
even though I claimed that it was the derived class pointer.

My sample code snippet follows:

//////////////////////////////////////////////////////////

#include <iostream>
#include <typeinfo>
#include <string>

using namespace std;

#define TYPEID(x) cout << #x << ": " << typeid(x).name() << endl;

class Base
{
public:
Base() {}
~Base() {}
virtual void testMethod (int x) {cout << "Passed value " << x << "
to the base class method\n";}
};

class Derived: public Base
{
public:
Derived() {}
~Derived() {}
virtual void testMethod (int x) {cout << "Passed value " << x << "
to the derived class method\n";}
};

template<typename T>
void
validateType(const string& label, T* ptr)
{
T* typePtr = dynamic_cast<T*>(ptr);
if(ptr)
cout << "Type match";
else
cout << "Type mismatch";
cout << " for " << label << endl;
}


main()
{
Base *basePtr1 = new Base;
Base *basePtr2 = new Base;
Derived *derivedPtr = static_cast<Derived *>(basePtr2);

basePtr1->testMethod(25);
derivedPtr->testMethod(25);

validateType<Base>("basePtr", basePtr1);
validateType<Derived>("Derived", derivedPtr);

TYPEID(basePtr1);
TYPEID(basePtr2);
TYPEID(derivedPtr);
return 0;
}


//////////////////////////////////////////////////////////

Thanks,
Song
u***@sta.samsung.com
2007-12-12 21:20:31 UTC
Permalink
Here is your mistake! It should be
if (typePtr)
Thanks. That is indeed a silly mistake. Any idea why RTTI is not
able to correctly identify the masquerading derived pointer (which is
actually the base type?)
Even after fixing the silly mistake (thanks for pointing it out), the
behavior remains the same. Here's the sample output:

Passed value 25 to the base class method
Passed value 25 to the base class method
Type match for basePtr
Type match for Derived
basePtr1: P4Base
basePtr2: P4Base
derivedPtr: P7Derived
Victor Bazarov
2007-12-12 21:55:22 UTC
Permalink
Post by u***@sta.samsung.com
Here is your mistake! It should be
if (typePtr)
Thanks. That is indeed a silly mistake. Any idea why RTTI is not
able to correctly identify the masquerading derived pointer (which is
actually the base type?)
Even after fixing the silly mistake (thanks for pointing it out), the
behavior remains the same.
The behaviour of your code was undefined since you 'static_cast' the
base class pointer to a derived class pointer when you have no right
to do so.

If you want to verify whether 'dynamic_cast' is working, here is
a simplified version:

template<class D, class B> bool verify(B* b) {
return dynamic_cast<D*>(b) != 0;
}

struct Base {
virtual ~Base() {}
};
struct Derived : Base {};

#include <iostream>
int main() {
Base *pReallyBase = new Base;
Base *pActuallyDerived = new Derived;
std::cout << "pReallyBase "
<< (verify<Derived>(pReallyBase) ? "is" : "isn't")
<< " in fact a Derived\n";
std::cout << "pActuallyDerived "
<< (verify<Derived>(pActuallyDerived) ? "is" : "isn't")
<< " in fact a Derived\n";
}

Output:
pReallyBase isn't in fact a Derived
pActuallyDerived is in fact a Derived
Post by u***@sta.samsung.com
Passed value 25 to the base class method
Passed value 25 to the base class method
Type match for basePtr
Type match for Derived
basePtr1: P4Base
basePtr2: P4Base
derivedPtr: P7Derived
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
James Kanze
2007-12-13 12:15:26 UTC
Permalink
Post by Generic Usenet Account
I ran a small experiment involving RTTI and dynamic casting.
Basically what I did was to cast a base class pointer to a derived
type (yes, I know that is not kosher). I then performed
dynamic_casting and I invoked RTTI. In both instances, the run time
environment did not pick up the fact that what I was claiming to be a
derived pointer was in fact the base pointer. However, when I invoked
a virtual method using the ostensibly derived class pointer (which was
in reality the base class pointer), it was the base class method that
got invoked (and correctly so). Is this the correct behavior, or is
it the quirk of my compiler (gcc version 3.3.1)?
dynamic_casting: Since the pointer was really to the base class, and
not to the derived class, as claimed, dynamic_casting should have
picked that up and returned NULL.
RTTI: The typeid for the pointer should have been that of the base,
even though I claimed that it was the derived class pointer.
//////////////////////////////////////////////////////////
#include <iostream>
#include <typeinfo>
#include <string>
using namespace std;
#define TYPEID(x) cout << #x << ": " << typeid(x).name() << endl;
class Base
{
Base() {}
~Base() {}
virtual void testMethod (int x) {cout << "Passed value " << x << "
to the base class method\n";}
};
The destructor should almost certainly be virtual as well.
Post by Generic Usenet Account
class Derived: public Base
{
Derived() {}
~Derived() {}
virtual void testMethod (int x) {cout << "Passed value " << x << "
to the derived class method\n";}
};
template<typename T>
void
validateType(const string& label, T* ptr)
{
T* typePtr = dynamic_cast<T*>(ptr);
This line has exactly the same effect as:
T* typePtr = ptr ;
How could the dynamic_cast ever fail. You have a pointer to a
T, and you ask if it really points to a T. If you have a
pointer to a T, either 1) it is a null ponter, 2) it really
points to an object of type T, 3) it points to one past the end
of an array of T objects, or 4) it points to nothing in
particular. dynamic_cast only has defined behavior in the first
two cases. It can't be made to work (and probably isn't very
useful, given that objects in arrays cannot really be
polymorphic) in the third case, and even reading the pointer
results in undefined behavior in the last case.
Post by Generic Usenet Account
if(ptr)
cout << "Type match";
else
cout << "Type mismatch";
cout << " for " << label << endl;
}
main()
{
Base *basePtr1 = new Base;
Base *basePtr2 = new Base;
Derived *derivedPtr = static_cast<Derived *>(basePtr2);
This is undefined behavior. You've lied to the compiler.
You've told it, unconditionally, that basePtr2 points to a
Derived. You've told the compiler that you know better.

Compilers don't like being lied to. They tend to get even with
you when you do. Except in exceptional cases, you should use
dynamic_cast when converting from pointer to base to pointer to
derived. If you used a dynamic_cast here, it would return a
null pointer. Using dynamic_cast is saying to the compiler: I
think this pointer really points to a Derived; please check, and
tell me if I'm wrong.
Post by Generic Usenet Account
basePtr1->testMethod(25);
derivedPtr->testMethod(25);
This line is undefined behavior. You're using the result of
your lies to the compiler, which is undefined behavior. If you
replace the static_cast above with a dynamic_cast, you're
dereferencing a null pointer, which is also undefined behavior.
Post by Generic Usenet Account
validateType<Base>("basePtr", basePtr1);
validateType<Derived>("Derived", derivedPtr);
Again: you pass derivedPtr to a function. That's undefined
behavior.
Post by Generic Usenet Account
TYPEID(basePtr1);
TYPEID(basePtr2);
TYPEID(derivedPtr);
As above.
Post by Generic Usenet Account
return 0;
}
--
James Kanze (GABI Software) email:***@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Loading...