Luca Fülbier Luca Fülbier - 3 months ago 17
C++ Question

Advantages of different methods for bypassing RTTI

Suppose i want a program that can manage different animals using a Base-Class/Interface

Animal
. The animals can have specific behavior (Fly/Climb/ThrowPoo). The goal is to iterate over a
list<Animal*>
and perform some action with them using their specific behavior.

The first thing that comes to mind is just casting the
Animal*
s using
dynamic_cast
or
static_cast
+
typeid
. But that approach is hard to maintain and can become quite slow if i have a bigger amount of objects to iterate over.

After some research i found the following ways to avoid RTTI and use virtual function calls instead:


  1. Declare every method an animal could possibly have as a virtual method in the
    Animal
    class.

  2. Use a dynamic-dispatch pattern, like the visitor pattern.



In my opinion both of these approaches have their own flaws. The first one pollutes the animal class and it's derived classes with a lot of abundant code/utility and you will need some methods like
bool canFly()
if you want to avoid specific function calls (If the function guarantees some behavior on call for example). I like the second approach a lot, but it is nowhere near as easy to read and understand as the previous one.

Can you elaborate the advantages/disadvantages of casting (RTTI), virtual functions and dynamic-dispatch patterns and when to properly use them? Also if i missed any methods or strategies please include them in the comparison.

Answer

I think that when you're thinking about dynamic_cast, you're envisioning the makeYourSound(Animal* a) function as consisting of two hundred if...else if blocks, each one testing for and handling a particular type of animal. Yeah, that's slow and unmaintainable. Don't do it.

What dynamic_cast is good for, really, is optional interfaces. A particular thing that some animals can do, but not others. if(Eagle* e = dynamic_cast<Eagle*>(a)) is a code smell, and a bad one. if(IFly* f = dynamic_cast<IFly*>(a)) smells much less. It avoids code duplication, it avoids polluting the base class with meaningless methods and tap-dancing around how to handle non-flying animals, and it describes more precisely in your code what you are trying to do.

The advantage of virtual function-based polymorphism is that the object is fully in charge, something that appeals greatly to OO purists. But in some cases, as a practial programmer, you don't want the object to be in control, just to tell your code a bit more about itself so it can be properly dealt with. More nuanced guidelines, like the OCP, are more widely applicable, and they don't rule out dynamic_cast, just indiscriminate use of it.

Comments