Q) Identifying the correct data type at run time (RTTI anddynamic_cast)

  • Thread starter Generic Usenet Account
  • Start date
G

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)?

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
 
V

Victor Bazarov

Generic said:
I ran [..]

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

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

using namespace std;

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

class Base
{
public:
Base() {}
~Base() {}

Probably should be

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

Newline in a string constant. Try to put more linebreaks in right
places when posting code.
};

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)

Here is your mistake! It should be

if (typePtr)
cout << "Type match";
else
cout << "Type mismatch";
cout << " for " << label << endl;
}


main()

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

Since 'basePtr2' is NOT 'Derived', any attempt to use 'derivedPtr'
has undefined behaviour.
basePtr1->testMethod(25);
derivedPtr->testMethod(25);

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

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


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

V
 
U

usenet

Generic Usenet Account wrote:
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?)
 
U

usenet

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
 
V

Victor Bazarov

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?)

I am not sure I understand the question. 'dynamic_cast' should
return a null pointer if the object is not of the type to which
you're trying to cast your expression.

V
 
V

Victor Bazarov

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
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

V
 
J

James Kanze

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";}
};

The destructor should almost certainly be virtual as well.
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);

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.
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.
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.
validateType<Base>("basePtr", basePtr1);
validateType<Derived>("Derived", derivedPtr);

Again: you pass derivedPtr to a function. That's undefined
behavior.
TYPEID(basePtr1);
TYPEID(basePtr2);
TYPEID(derivedPtr);

As above.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,128
Latest member
ElwoodPhil
Top