Why is dynamic_cast evil or not ? Should I use dynamic_cast in this case?
Asked Answered
D

3

12

Some say the use of dynamic_cast often means bad design and dynamic_cast can be replaced by virtual functions

  1. why is the use of dynamic_cast considered bad design?
  2. Suppose I have I function name func(Animal* animal, int animalType) , the implementation in func is like:

    bool func(Animal* animal, int animalType)
    {
      ...
      /* Animal is the base class of Bear, Panda, Fish ....
      dynamic_cast animal to real animals(Bear, Panda, Fish...) 
      according to animalType. Do some processing with this specific
      type of animal, using its additional information beyond base 
      class Animal. */
    }
    

Is this case a proper use of dynamic_cast?

Dung answered 26/8, 2013 at 8:0 Comment(2)
dynamic_cast is "evil" because it's slow/overkill. Aside from that, use the tool that solves your problem. End of story.Alexio
The whole point of dynamic polymorphism and virtual functions in particular is that you don't have to care for the exact type anymore, but only have to know that it is a kind of some base type that provides a certain interface, however that may be implemented by the particular derived type. dynamic_cast is "bad design" for the simple reason that it violates this purpose, since you need your object to be of some derived type, so it doesn't suffice to know the base type of it. That being said it still has its use (especially as the world isn't as simple as Java likes it to be).Vlad
T
20

This is EXACTLY the wrong place to use dynamic_cast. You should be using polymorphism. Each of the Animal classes should have a virtual function, say, process and here you should just call animal->process().

class Animal {
    virtual void Process() = 0;
}

class Cat : public Animal {
    void Process() { std::cout << " I am a tiny cat"; }
}

class Bear : public Animal {
    void Process() { std::cout << "I am a big bear"; }
}

void func(Animal * animal) {
    if (animal != nullptr) { animal->Process(); }
}

Other problems.

What if animal is a Dog, but due to a bug animal_type says its a Cat?

There are times when static_cast is necessary, and if possible use it instead of dynamic_cast. Dynamic cast has the additional performance cost that static cast does not. For this, you need to be sure you know the type that is coming in, since static_cast is more unsafe.

At the very least, animal_type should be a member of Animal.

Trimer answered 26/8, 2013 at 8:4 Comment(10)
What if he wants some animals to fly ?Cindicindie
@Cindicindie multiple levels of inheritance, or const bool can_flyTrimer
"multiple levels of inheritance" and he's back to dynamic_castCindicindie
@Cindicindie What do you mean? I mean Animal -> FlyingAnimal -> ParrotTrimer
What if he has a vector of animal pointers? (which is most likely) He'll still have to use dynamic_cast thenCindicindie
@gx_ ya.. I wrote that out as a return statement, since the orig func was bool, then I got lazy..Trimer
@Cindicindie depending on the exact needs there are several ways, simplest might be another vector<FlyingAnimal*> which points to the subset. A common function such as update might handle flying for some, walking for others. fly function could be present in all animals but is an empty function if cannot fly... etc etcTrimer
@user1233963: normally, it should not matter. The point is, you should not have a fly method. fly is a mean to an end, and methods in the base class should express the end. Thus, you would have a Animal::moveToward(Point const& p); and it will throw if p cannot be reached (because it's underwater, because it's too high, because the door is locked...)Ubangi
@KarthikT I'm curious about what is the right place to use dynamic_cast, can you offer some examples?Dung
@GaryGauh It is hard to give examples, but as Matthieu M. suggests, it would be required if redesign is not possible, in the case of 3rd party libraries, or infeasible, in case of legacy code, or an extremely limited use case which does not merit a large redesign (be careful of the last option, that use case might become common enough and then you should prefer to redesign, rather than keep using dynamic_cast.)Trimer
U
5

In theory, down-casting should never be necessary. Instead you should adapt the base class to include the necessary virtual method.

In practice, you encounter things such as 3rd party libraries. In this case, modifying the base class is not an option and thus you may be forced into using dynamic_cast...

Back to your example:

class Animal {
public:
    // starts moving toward `p`,
    // throws a `Unreachable` exception if `p` cannot be reached at the moment.
    virtual void moveToward(Point const& p) = 0;
}; // class Animal

And then:

bool move(Animal& animal, Point const& p) {
    try {
        animal.moveToward(p);
        return true;
    } catch (Unreachable const& e) {
        LOG(animal.id() << " cannot reach " << p << ": " << e.what());
    }

    return false;
} // move
Ubangi answered 26/8, 2013 at 8:55 Comment(10)
Your theory is wrong. There are cases where downcasting is the preferred solution. (Not his example, of course.) If you're transporting data accross a low level channel, for example, in which the transport channel has no knowledge of the higher level interfaces. Or even is you have a class which implements several interfaces: dynamic_cast (with pointers) is the standard way of asking whether the actual object implements the extended interface or not.Subadar
@JamesKanze: The problem with down-casting is that the set of types to which you can down-cast is potentially unbounded. This is why it is generally met with a doubt: when a specific process is done on an object through dynamic_cast, you cannot know whether there are other types where such process should also be done.Ubangi
If the number of types you might want to down-cast to is potentially unbounded, then you shouldn't be using dynamic_cast. On the other hand, if it's just to get at an extended interface, there's only one type, and if it is in the client end of a transport channel, the client knows what type it expects (even if the transport channel doesn't), and the fact that the object has been routed to it means that if the downcast fails, there has been an error.Subadar
I believe in C++ dynamic_cast is the only effective way to perform a cross-cast (as opposed to a downcast). Though there are very few reasons I can think of where a cross-cast is the ideal design, however implementations of an acyclic-visitor come to mind.Stenographer
Although this question is old, I wanna put my two cents on this and say: I never understand people who say "down-casting should never be necessary". Of course, not for this particular case, as James said, but again, if you design plugins, or if you deal with any highly OO library (such as Qt), how would you avoid dynamic cast? Qt provides qobject_cast, for performance reasons, but if that doesn't exist, then dynamic_cast is the only way to make any sense of any generic QObject, which usually is stored in an array of abstract members for methods like findChildren().Aurelea
@TheQuantumPhysicist: The problem of down-casting is that it breaks the Liskov Substitution Principle (a cornerstone of OO). For example, if your function takes a Base* but has a different behavior if the object is a Derived, then when I wrap Base* in a LoggingDecorator for debugging purposes, even though LoggingDecorator wraps an actual Derived I get the non-specific behavior! That's nasty! So to answer your question, if the only thing you have is a QObject, then the only thing you should do with it is call a QObject method. And if you cannot do your work, the API sucks.Ubangi
@MatthieuM. Don't you think that dynamic_cast solves this problem by telling you whether your cast is correct or incorrect? You cast, it doesn't throw and doesn't return nullptr, and that means you didn't do anything wrong. What's the problem with that? (ignoring performance issues, which is not always a concern, I'm just talking about the big "never" you guys throw around).Aurelea
@TheQuantumPhysicist: No, downcasts (such as dynamic_cast) break the Liskov Substitution Principle, so they are the problem, they do not solve it. As mentioned, downcasts break when decorators or mocks are involved, for example; and that's because they break encapsulation. They break the fundamental principle that an object should be the one dictating its behavior. At this point, safety and performance are secondary concerns; functionality is at stake!Ubangi
@MatthieuM. Well, I don't know what to say, since you seem insisting on this. Just keep in mind that the most famous cross-platform GUI tool in the world (Qt) sucks, in your own words. In my opinion, a principle is a principle, not a law. It doesn't have to be strictly followed. It's just an idea that has implications. Cheers!Aurelea
@TheQuantumPhysicist: Well, I consider that C++ "sucks" if that eases your mind :) Qt is widely used not because it's a jewel of architecture, but because it works quite well; I doubt the Qt designers would argue that its API is perfect, but once something is widely used, backward compatibility trumps perfection. It's alright, it's one of the trade-offs of engineering. Which is exactly what I am saying in my answer: you shouldn't design an API to require downcasting, but if you are stuck with an existing API that cannot be modified for some reason, then you may need to hack around its limits.Ubangi
R
0

When you use downcasting, dynamic_cast is good, because it restricts you to downcast to irrelevant type. Please refer this.

Ruler answered 26/8, 2013 at 8:13 Comment(5)
Actually, I think you meant relevant; also... this is a rather crude answer. Link-only answers are discouraged because links break routinely and then... your answer would be worthless. Links should thus be used as additional references, if you do not know how else to explain thing you can always sum up what the link is saying.Ubangi
@MatthieuM. May be you are right, but link is of my own article, But I should have added some peace here also.Ruler
I know the article, commented on it on reddit over the week-end ;)Ubangi
@MatthieuM. I have added one comment to your comments in reddit, kindly see, whenever you find time.Ruler
@MatthieuM. The article explains what a dynamic_cast is. It doesn't address the question as to when it is good or bad to use one. As such, it doesn't address the OP's question (nor does it pretend to).Subadar

© 2022 - 2024 — McMap. All rights reserved.