4

I have a series of classes representing "smart" map elements: MapTextElement, MapIconElement, etc. The classes are extending various Qt graphics item classes, but also provide common functionality, such as an abstract factory method that returns a property panel specialized for each class. I have declared these common methods in a pure virtual class, MapElementInterface. My classes then multiply-inherit the appropriate Qt base class as well as the interface:

class MapTextElement : public QGraphicsTextItem, public MapElementInterface
class MapIconElement : public QGraphicsItem, public MapElementInterface

So my class hierarchy looks kind of like:

         +-------------+    +-------------------+
         |QGraphicsItem|    |MapElementInterface|
         +-------------+    +-------------------+
                ^                   ^   ^
                |                   |   |
         +------+------+            |   |    
         |             |            |   |
+-----------------+   +--------------+  |
|QGraphicsTextItem|   |MapIconElement|  |
+-----------------+   +--------------+  |
     ^                                  |
     |                                  |
     +-------------------+        +-----+
                         |        |
                      +--------------+
                      |MapTextElement|
                      +--------------+

I am receiving a pointer to a QGraphicsItem from a Qt-provided method. In this case, I know that the pointer is not only QGraphicsItem, but also MapElementInterface. I want to treat the pointer as a MapElementInterface.

QList<QGraphicsItem*> selected = scene_->selectedItems();
if (selected.count() == 1) {
  // We know that the selected item implements MapEditorInterface
  MapElementInterface *element = SOME_CAST_HERE<MapElementInterface*>(selected[0]);
  QWidget *panel = element->GeneratePropertyPanel(property_dock_);
}

What is the proper cast to use? Or am I going about this completely the wrong way?

Dave Mateer
  • 17,608
  • 15
  • 96
  • 149
  • You Can't cast horizontally across your class hierarchy as QGraphicsItem and MapElementInterface are not related. You must cast back up to a derived type then back down (using dynamic_cast) – Martin York Jul 13 '10 at 21:50
  • @Martin: It does *seem* to be working using a direct dynamic_cast. Could you point me to an article online that explains in more detail the problems or "gotchas" you are anticipating? Thanks! – Dave Mateer Jul 14 '10 at 12:49
  • When I try to dynamic cast between two types that are not related 'QGraphicsItem and MapElementInterface' it returns a NULL pointer. Therefore you must be doing something else or there is some relationship between the two. You can cast from one to the other only by casting (dynamic_cast) up the hierarchy then down the hierarchy. If you are using another cast (rinterpret_cast) then you are in undefined territory. – Martin York Jul 14 '10 at 14:32
  • @Martin: Could you let me know what compiler/runtime you are using? Maybe the behavior we are seeing changes depending on the environment. That would be good to know. Also, you are trying the dynamic_cast from a *pointer* to the *multiply-inherited* object as in my example, right? – Dave Mateer Jul 15 '10 at 12:40
  • gcc (3.? and 4.2) and dev studio 10. But this is what dynamic_cast<> should do. If there is no relationship between the types (as there is above) then it must return NULL. So you are either using a different cast (which is wrong) or there is another relationship between the types that you have not mentioned or you are doing multiple casts up and down the tree. – Martin York Jul 15 '10 at 14:19

4 Answers4

4

With multiple inheritance, dynamic_cast is the only way, and check the return value against NULL.

Frederik Slijkerman
  • 6,471
  • 28
  • 39
3

Someone posted a good explanation here.

When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?

Community
  • 1
  • 1
Shawn D.
  • 7,895
  • 8
  • 35
  • 47
2

You have to be careful here, because given the diagram your pointer could point to either MapTextElement or MapIconElement. The only safe bet for you here is to go with dynamic casting. Or provide another way for yourself to figure the object's type.

Vlad
  • 9,180
  • 5
  • 48
  • 67
1

You can cast to MapIconElement, and then to MapElementInterface, or you can cast to MapTextElement, then to MapElementInterface. You must chose a path (or dynamic-down cast to check what path you take).

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • Can you clarify? Are you saying that there no way to directly cast to MapElementInterface? If that is the case, then every time I add a new concrete class of MapElementInterface, I have to add another `if` path to my code. That smells, and largely defeats the purpose of the interface. – Dave Mateer Jul 13 '10 at 17:21
  • Exactly. I'm afraid you have to dynamic_cast downwards (indeed not cool), or turn MapElementInterface into a derived class of QGraphicsItem. Or maybe turn MapElementInterface inheritance into some member of ...Element. – Alexandre C. Jul 13 '10 at 18:35
  • `dynamic_cast` seems to be working for me, so I must not be running into the problem you are describing. Thanks anyway for the help! – Dave Mateer Jul 13 '10 at 20:58