Hi ! Please! help me about RTTI

In Thinking in C++, I try to run a example code, but it didnt give a correct result.
I found the bug point is “(typeid(s) == typeid(cp)”. it always zero. But Why???
I replaced a new expression ----"(typeid(*s) == typeid(Circle)" , it runs OK.
By the way, My envirnment is Code::Blocks + gnu.
Chaos haha:joy:

the code running OK.

#include <iostream>
#include <typeinfo>
using namespace std;

class Shape { public: virtual ~Shape() {}; };
class Circle : public Shape {};
class Square : public Shape {};

int main() {
    Circle c;
    Shape* s = &c;
    Circle* cp = 0;
    Square* sp = 0;
    if(typeid(*s) == typeid(Circle)){
        cp = static_cast<Circle*>(s);
    }
    if(typeid(*s) == typeid(Square)){
        sp = static_cast<Square*>(s);
    }
    if(cp != 0)
        cout << "It's a circle!" << endl;
    if(sp != 0)
        cout << "It's a square!" << endl;
}

The simple answer: the code in your book is wrong.

  • The type of s will always be Shape *.
  • The type of cp will always be Circle *.
  • These two types are not equivalent.

You can see this by running the following:

#include <iostream>
#include <typeinfo>

class Shape
{
public:
	virtual ~Shape() = default;
};

class Circle : public Shape
{
};

class Square : public Shape
{
};

int main()
{
	Circle circle;
	Square square;

	Circle * circlePointer = &circle;
	Square * squarePointer = &square;

	Shape * shapePointer = &square;

	const std::type_info & typeOfCircle = typeid(circle);
	std::cout << typeOfCircle.name() << '\n';

	const std::type_info & typeOfSquare = typeid(square);
	std::cout << typeOfSquare.name() << '\n';

	const std::type_info & typeOfCirclePointer = typeid(circlePointer);
	std::cout << typeOfCirclePointer.name() << '\n';

	const std::type_info & typeOfSquarePointer = typeid(squarePointer);
	std::cout << typeOfSquarePointer.name() << '\n';

	const std::type_info & typeOfShapePointer = typeid(shapePointer);
	std::cout << typeOfShapePointer.name() << '\n';

	const std::type_info & typeOfObjectAtShapePointer = typeid(*shapePointer);
	std::cout << typeOfObjectAtShapePointer.name() << '\n';

	std::cin.get();
}

(std::cin.get(); is there to stop the program ending after printing.)

.name() returns an ‘implementation defined’ type name,
which means it will be different depending which compiler you use.
Using Visual Studio 2015 I get the following output:

class Circle
class Square
class Circle *
class Square *
class Shape *
class Square

Which is to say that:

  • The type of circle is Circle
  • The type of square is Square
  • The type of circlePointer is Circle *
  • The type of squarePointer is Square *
  • The type of shapePointer is Shape *
  • The type of *shapePointer is Square

In other words, the objects are like this:
Shapes

Which demonstrates that it is the type of the object being pointed to that you must inspect,
not the type of the pointer.
Type of the pointer does not change, but what the pointer points to does change.

Furthermore, if you use typeid on an expression then the object returned by that expression must be valid, so you cannot do typeid(*cp) if cp is a null pointer.

Edit:

More specifically, typeid(*cp) would throw a std::bad_typeid.
As stated by cppreference:

If the glvalue expression is obtained by applying the unary * operator to a pointer and the pointer is a null pointer value, an exception of type std::bad_typeid or a type derived from std::bad_typeid is thrown.


A few miscellaneous comments:

  • The expression typeid(s) == typeid(cp) is not ‘zero’, it is false. Zero and false are two different things.
  • The semicolon in virtual ~Shape() {}; is unnecessary.
  • Don’t use std::endl to end a line when using std::cout, use '\n' instead.
    • As stated by cppreference: “Use of std::endl in place of '\n', encouraged by some sources, may significantly degrade output performance.”
  • Do not assign 0 to pointers and do not compare pointers to 0, use the nullptr constant instead, or if you need C++98/C++03 compatibility, use NULL from <cstddef>.
  • using namespace std; is bad practice, prefer to fully qualify everything or use using std::cout, using std::cin etc. from within main.
  • Avoid using single-letter variable names, prefer to use meaningful names.
4 Likes

Thanks Pharap!!!!!! I really appreciate you !!!!!!

2 Likes