10

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?

Community
  • 1
  • 1
Gary Gauh
  • 4,984
  • 5
  • 30
  • 43
  • `dynamic_cast` is "evil" because it's slow/overkill. Aside from that, use the tool that solves your problem. End of story. – user541686 Aug 26 '13 at 08:07
  • 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). – Christian Rau Aug 26 '13 at 09:44

3 Answers3

19

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.

Pharap
  • 3,826
  • 5
  • 37
  • 51
Karthik T
  • 31,456
  • 5
  • 68
  • 87
  • What if he wants some animals to fly ? – user1233963 Aug 26 '13 at 08:08
  • @user1233963 multiple levels of inheritance, or `const bool can_fly` – Karthik T Aug 26 '13 at 08:09
  • 4
    "multiple levels of inheritance" and he's back to dynamic_cast – user1233963 Aug 26 '13 at 08:12
  • @user1233963 What do you mean? I mean `Animal` -> `FlyingAnimal` -> `Parrot` – Karthik T Aug 26 '13 at 08:15
  • What if he has a vector of animal pointers? (which is most likely) He'll still have to use dynamic_cast then – user1233963 Aug 26 '13 at 08:17
  • @gx_ ya.. I wrote that out as a return statement, since the orig func was `bool`, then I got lazy.. – Karthik T Aug 26 '13 at 08:18
  • 1
    @user1233963 depending on the exact needs there are several ways, simplest might be another `vector` 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 etc – Karthik T Aug 26 '13 at 08:21
  • 8
    @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...) – Matthieu M. Aug 26 '13 at 08:45
  • @KarthikT I'm curious about what is the right place to use `dynamic_cast`, can you offer some examples? – Gary Gauh Sep 09 '13 at 03:11
  • @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`.) – Karthik T Sep 09 '13 at 04:54
4

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
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 3
    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. – James Kanze Aug 26 '13 at 10:22
  • 1
    @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. – Matthieu M. Aug 26 '13 at 12:05
  • 4
    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. – James Kanze Aug 27 '13 at 12:00
  • 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. – YoungJohn Aug 14 '15 at 17:04
  • 3
    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()`. – The Quantum Physicist Jul 11 '17 at 07:32
  • @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. – Matthieu M. Jul 11 '17 at 09:30
  • @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). – The Quantum Physicist Jul 11 '17 at 09:33
  • @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! – Matthieu M. Jul 11 '17 at 10:43
  • 1
    @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! – The Quantum Physicist Jul 11 '17 at 10:59
  • @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. – Matthieu M. Jul 11 '17 at 11:34
0

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

Pranit Kothari
  • 9,721
  • 10
  • 61
  • 137
  • 4
    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. – Matthieu M. Aug 26 '13 at 08:47
  • @MatthieuM. May be you are right, but link is of my own article, But I should have added some peace here also. – Pranit Kothari Aug 26 '13 at 09:30
  • I know the article, commented on it on reddit over the week-end ;) – Matthieu M. Aug 26 '13 at 09:42
  • @MatthieuM. I have added one comment to your comments in reddit, kindly see, whenever you find time. – Pranit Kothari Aug 26 '13 at 09:57
  • @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). – James Kanze Aug 26 '13 at 10:24