سؤال

I have heard a lot of people say any usage of typeid is bad design, yet to me it seems like it offers great utility.

  1. When (and why) is usage of typeid "bad design"?
  2. When is usage of typeid acceptable?
  3. When it is not acceptable, but you still need something alike, what would an alternative be that would have good design?
هل كانت مفيدة؟

المحلول

The problem isn't with typeid. The problem is that seeing typeid would encourage you to write this:

PolymorphicType *pType = ...;
if(typeid(*pType) == typeid(Derived1))
  pType->Func1();
else if(typeid(*pType) == typeid(Derived2))
  pType->Func2();
else if(typeid(*pType) == typeid(Derived3))
  pType->Func3();

This is what we call "really stupid". This is a virtual function call done in about the least reasonable way possible. typeid has the potential for abuse when used to replace dynamic_cast and virtual functions.

This example may sound far-fetched. After all, it's obvious that this is just a virtual call. But bad code often grows from the path of least resistance; all it takes is for one person to do a typeid() == typeid(), and the seed of this code has begin. In general, if you're directly using typeid frequently, odds are very good that you're doing something that could be better done with other language constructs.

typeid is the polymorphic type deduction method of last resort.

Is all usage of typeid wrong? Of course not. boost::any wouldn't be possible without it. Well it would be possible, but it wouldn't be any safer than void*. typeid is what makes type-safe boost::any type erasure possible. There are other legitimate uses of it.

But in code-lines-to-use ratio, I would suggest it should be in about one in 10,000 lines of code, at most. Much fewer than that, and you're probably using it wrong.

Is type checking slow?

In general, the main reason to call typeid is either in templated code (as in boost::any) or when you're expecting a polymorphic type. If the type is statically determined (ie: a typename or value of a non-polymorphic type is given), then you can expect it to be done at compile-time.

It's polymorphic values you should be concerned about. I've seen a performance test that showed that some typeid implementations actually walk the class hierarchy, so the time it takes for them to find the type is proportional to the number of classes between the real type and the given type. Every implementation will be different, but that's a pretty good indication that maybe you shouldn't put it in performance-critical code.

نصائح أخرى

I don't think anyone says that usage of typeid is itself bad design; rather, what you'll hear is that usage of typeid is indicative of bad design. The idea there is that any logic that distinguishes (say) Square from Circle should actually be in those classes, so should be expressed by a virtual function (polymorphism).

Needless to say, this is not an absolute rule.

It's too bad that those who gave you this good advice couldn't give the reasons for it. It means they're simple cargo cult programmers.

All expressions have a static compile-time type (after template expansion). All types are either polymorphic or not.

I'll try to elaborate for you why typeid is (the closest thing to) useless in both cases:

  1. There is no reason to use typeid when the expression's static type is not polymorphic. The compiler will use the static type, not the dynamic type, so compile-time function overloading works. And type identity can be checked much more efficiently using a templated type trait such as is_same.

  2. If the static type of the expression is polymorphic, then typeid fails. Yes, it will give information on the dynamic type of the object. But only the most derived dynamic type. This violates the Liskov Substitution Principle, since given types Base and Child, adding a new type Grandchild derived from Child will not be treated as Child is. The Open-Closed principle is also violated, since the design cannot be extended with new types without rewriting everything. There is no encapsulation, since information about the class hierarchy must be scattered throughout the program, creating strong coupling. dynamic_cast overcomes some but not all of these problems.

In short, a program that uses typeid will be very difficult to maintain and test.

And, as with any rule, there may be limited exception. Where you're using OO language features of inheritance and virtual functions, but you don't actually want a polymorphic design. I can think of exactly one: where you want to log the name of the type as a string, using type_info::name(). You might also be able to use it to detect slicing (where polymorphism is already violated). But this is good for debugging only, never for controlling program flow.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top