Practical use of dynamic_cast?

I have a pretty simple question about dynamic_cast . I know this is used to identify the type of runtime, i.e. Be aware of the type of object at run time. But from your programming experience, can you give a real scenario when you had to use this operator? What were the difficulties without using it?

+49
c ++ casting rtti
Aug 01 2018-12-12T00:
source share
9 answers

Toy example

Noah's Ark will function as a container for different types of animals. Since the ark itself does not care about the difference between monkeys, penguins and mosquitoes, you define the Animal class, deduce the Monkey , Penguin and Mosquito classes from it and save each of them as a Animal in the ark.

Once the flood is over, Noah wants to distribute the animals on the ground to the places where they are, and therefore needs additional knowledge about the common animals stored in his ark. As one example, he can now try the dynamic_cast<> each animal before Penguin to find out which animal is the penguin to be released in the Antarctic and which not.

Real life example

We have implemented an event monitoring environment in which the application will store events created at runtime in a list. Event monitors will go through this list and study those specific events that they were interested in. Event types were the same as SYSCALL , FUNCTIONCALL and INTERRUPT , such as the OS-level.

Here we have saved all our specific events in the general list of Event instances. Then the monitors will iterate over this list and dynamic_cast<> events that they saw in those types that were of interest to them. All others (those that throw an exception) are ignored.

Question: Why do not you have a separate list for each type of event?

Answer. You can do this, but it makes system expansion with new events, as well as new monitors (aggregation of several types of events) more difficult, because everyone needs to know about the appropriate lists for checking.

+49
Aug 01 2018-12-12T00:
source share

A typical use case is a visitor pattern:

 struct Element { virtual ~Element() { } void accept(Visitor & v) { v.visit(this); } }; struct Visitor { virtual void visit(Element * e) = 0; virtual ~Visitor() { } }; struct RedElement : Element { }; struct BlueElement : Element { }; struct FifthElement : Element { }; struct MyVisitor : Visitor { virtual void visit(Element * e) { if (RedElement * p = dynamic_cast<RedElement*>(e)) { // do things specific to Red } else if (BlueElement * p = dynamic_cast<BlueElement*>(e)) { // do things specific to Blue } else { // error: visitor doesn't know what to do with this element } } }; 

Now if you have Element & e; , you can do MyVisitor v; and say e.accept(v) .

The main design feature is that if you change the Element hierarchy, you only need to edit your visitors. The template is still quite complex and is only recommended if you have a very stable class hierarchy of Element s.

+10
Aug 01 2018-12-12T00:
source share

Imagine this situation: you have a C ++ program that reads and displays HTML. You have an HTMLElement base class that has a pure virtual displayOnScreen method. You also have a renderHTMLToBitmap function that draws HTML into a bitmap. If each HTMLElement has vector<HTMLElement*> children; , you can simply pass in an HTMLElement representing the <html> element. But what if some of the subclasses need special treatment, such as <link> for adding CSS. You need a way to find out if the element is a LinkElement , so you can pass it to CSS functions. To find out, you must use dynamic_cast .

The problem with dynamic_cast and polymorphism in general is that it is not very efficient. When you add vtables to the mix, it only gets worse.

When you add virtual functions to the base class when they are called, you end up typing quite a few layers of function pointers and memory areas. It will never be more efficient than something like an ASM call statement.

Edit: in response to Andrew's comment below, here's a new approach: instead of dynamically casting for a specific type of element ( LinkElement ) instead, you have another abstract subclass of HTMLElement called ActionElement , which overrides displayOnScreen with a function that doesn't display anything, and creates new pure virtual function: virtual void doAction() const = 0 . dynamic_cast changed to check for an ActionElement and just calls doAction() . You will have the same subclass for GraphicalElement with the virtual displayOnScreen() method.

Edit 2: The โ€œrenderingโ€ method might look like this:

 void render(HTMLElement root) { for(vector<HTLMElement*>::iterator i = root.children.begin(); i != root.children.end(); i++) { if(dynamic_cast<ActionElement*>(*i) != NULL) //Is an ActionElement { ActionElement* ae = dynamic_cast<ActionElement*>(*i); ae->doAction(); render(ae); } else if(dynamic_cast<GraphicalElement*>(*i) != NULL) //Is a GraphicalElement { GraphicalElement* ge = dynamic_cast<GraphicalElement*>(*i); ge->displayToScreen(); render(ge); } else { //Error } } } 
+3
Aug 01 2018-12-12T00:
source share

The dynamic_cast operator solves the same problem as dynamic sending (virtual functions, visitor template, etc.): it allows you to perform different actions based on the type of execution time of the object.

However, you should always give preference to dynamic sending, except, possibly, when the number of dynamic_cast that you need will never grow.

Eg. you should never do:

 if (auto v = dynamic_cast<Dog*>(animal)) { ... } else if (auto v = dynamic_cast<Cat*>(animal)) { ... } ... 

for convenience and performance, but you can do, for example.

 for (MenuItem* item: items) { if (auto submenu = dynamic_cast<Submenu*>(item)) { auto items = submenu->items(); draw(context, items, position); // Recursion ... } else { item->draw_icon(); item->setup_accelerator(); ... } } 

which I found very useful in this particular situation: you have one very specific sub-hierarchy that needs to be handled separately, this is where dynamic_cast shines. But real-world examples are pretty rare (an example of a menu is something I had to deal with).

+2
Aug 2 2018-12-12T00:
source share

dynamic_cast is not intended as an alternative to virtual functions.
dynamic_cast has non-trivial overhead (or, I think, that way), since you need to go through the entire class hierarchy.
dynamic_cast is similar to the C # 'is' statement and QueryInterface of the old old COM.

So far, I have found one real use of dynamic_cast:
(*) You have multiple inheritance , and in order to find the target of the throw, the compiler must go through the class hierarchy up and down to find the target (or down and up if you want). This means that the cast target is in a parallel branch with respect to where the translation source is in the hierarchy. I think there is no other way to make such a throw.

In all other cases, you simply use some virtual base class to tell you what type of object you have, and ONLY THEN you dynamic_cast it to the target class so that you can use some of its non-virtual functions. Ideally, there shouldn't be any virtual functions, but what the hell do we live in the real world.

Doing things like:

  if (v = dynamic_cast(...)){} else if (v = dynamic_cast(...)){} else if ... 

- This is a waste of productivity.

+1
Aug 08 2018-12-12T00:
source share

Castings should be avoided if possible, because it basically tells the compiler that you know better, and this is usually a sign of some weakening of the design.

However, you may encounter situations where the abstraction level was too high for 1 or 2 subclasses, where you have the choice to change the design or solve it by checking the subclass with dynamic_cast and processing it in a separate branch. Trading involves adding extra time and risk now against additional maintenance issues later.

0
Aug 01 2018-12-12T00:
source share

In most cases, when you write code in which you know the type of object you are working with, you simply use static_cast as it is more efficient.

Situations in which you need dynamic casting usually come (in my experience) due to a lack of forethought in design - usually where the developer fails to provide an enumeration or identifier that allows you to determine the type later in the code.

For example, I have already seen this situation in more than one project:

You can use a factory where internal logic decides which derived class the user wants, rather than the user explicitly choosing one. This factory, in an ideal world, returns an enumeration that will help you identify the type of returned object, but if it is not, you may need to check what type of object it gave you using dynamic_cast.

Your next question will obviously be this: why do you need to know the type of object that you use in your code using factory?

In an ideal world, you would not do this - the interface provided by the base class would be sufficient to manage all returned factory objects to all required extents. However, people do not create design. For example, if your factory creates abstract connection objects, you may suddenly realize that you need to access the UseSSL flag in your socket connection object, but the factory base does not support this and is not related to any of the other classes that use the interface. That way, maybe you can check if you want to use this type of derived class in your logic, and also set / set the flag directly if you are.

It is ugly, but it is not an ideal world, and sometimes you do not have time to reorganize an imperfect design completely in the real world under the pressure of work.

0
Aug 01 2018-12-12T00:
source share

Contract programming and RTTI shows how you can use dynamic_cast to let objects advertise which interfaces they implement. We used it in my store to replace a rather opaque meta-object system. Now we can clearly describe the functionality of the objects, even if the objects are introduced by the new module a few weeks / months after the platform was โ€œbakedโ€ (although, of course, the contracts had to be resolved up).

0
Aug 01 '12 at 18:28
source share

The dynamic_cast operator is very useful to me. I especially use it with an observer pattern for event management :

 #include <vector> #include <iostream> using namespace std; class Subject; class Observer; class Event; class Event { public: virtual ~Event() {}; }; class Observer { public: virtual void onEvent(Subject& s, const Event& e) = 0; }; class Subject { private: vector<Observer*> m_obs; public: void attach(Observer& obs) { m_obs.push_back(& obs); } public: void notifyEvent(const Event& evt) { for (vector<Observer*>::iterator it = m_obs.begin(); it != m_obs.end(); it++) { if (Observer* const obs = *it) { obs->onEvent(*this, evt); } } } }; // Define a model with events that contain data. class MyModel : public Subject { public: class Evt1 : public Event { public: int a; string s; }; class Evt2 : public Event { public: float f; }; }; // Define a first service that processes both events with their data. class MyService1 : public Observer { public: virtual void onEvent(Subject& s, const Event& e) { if (const MyModel::Evt1* const e1 = dynamic_cast<const MyModel::Evt1*>(& e)) { cout << "Service1 - event Evt1 received: a = " << e1->a << ", s = " << e1->s << endl; } if (const MyModel::Evt2* const e2 = dynamic_cast<const MyModel::Evt2*>(& e)) { cout << "Service1 - event Evt2 received: f = " << e2->f << endl; } } }; // Define a second service that only deals with the second event. class MyService2 : public Observer { public: virtual void onEvent(Subject& s, const Event& e) { // Nothing to do with Evt1 in Service2 if (const MyModel::Evt2* const e2 = dynamic_cast<const MyModel::Evt2*>(& e)) { cout << "Service2 - event Evt2 received: f = " << e2->f << endl; } } }; int main(void) { MyModel m; MyService1 s1; MyService2 s2; m.attach(s1); m.attach(s2); MyModel::Evt1 e1; e1.a = 2; e1.s = "two"; m.notifyEvent(e1); MyModel::Evt2 e2; e2.f = .2f; m.notifyEvent(e2); } 
0
Aug 08 2018-12-12T00:
source share



All Articles