3
votes

I recently met a strange problem of my little program and it would be great if you help me to get the reason of this behavior.

My task is quiet simple - I want to use Qt Graphics Framework to show some objects and I want Box2D to calculate bodies position. So my class hierarchy looks like the following:

I have 1 base abstract class B2DObject. It contains some Box2D staff + some common parameters for its successors (names, some flags, etc.). It also has couple of pure virtual functions that will be reimplemented in successor classes.

Then I implement some classes that represent basic shapes: circles, rectangles, polygons, etc. I am doing it in the following way:

class ExtendedPolygon : public B2DObject, public QGraphicsPolygonItem { ... };
class ExtendedCircle : public B2DObject, public QGraphicsEllipseItem { ... };

etc. (for those who are not familiar with Qt, QGraphics***Item is inherited from QGraphicsItem).

Also I inherited QGraphicsScene and reimplemented its mousePressEvent. In this function I request an object placed at some point on the screen using QGraphicsScene::itemAt function (which returns QGraphicsItem*), convert it to B2DObject* and try to get some internal field from this object:

void TestScene::mousePressEvent (QGraphicsSceneMouseEvent *event)
{
    QGraphicsItem* item = itemAt (event->scenePos ());
    if (item)
    {
        B2DObject* obj = reinterpret_cast < B2DObject* > (item);
        QString objName = obj->Name();  // just for example, 
                                        // getting other internal fields has 
                                        // the same effect (described below)
        // use retrieved field somehow (e.g. print in the screen)
    }

    // give the event to the ancestor
}

Unfortunately, dynamic_cast will not work here because these classes are completely unrelated.

Then I create necessary objects and add it to my scene:

ExtendedPolygon* polygon = new ExtendedPolygon (parameters);
polygon->setName (QString ("Object 1"));
...
TestScene scene;
scene.addItem (polygon);

(for those who are not familiar with Qt, here is the prototype of the last function:

void QGraphicsScene::addItem(QGraphicsItem *item);

I guess it just stores all items in internal index storage and calls QGraphicsItem::paint (...) when item needs to be repainted. I suppose QGraphicsScene doesn't make any significant changes to this item).

So my problems start when I run the program and click on an item on the screen. TestScene::mousePressEvent is called (see a piece of code above).

Mouse click position is retrieved, item is found. Casting works fine: in the debugger window (I'm using Qt Creator) I see that obj points to ExtendedPolygon (address is the same as when I add the item to the scene and in the debugger window I can see all the fields). But when I get some field, I receive garbage in any case (and it does not matter, what I'm trying to get - a QString or a pointer to some other structure).

So first of all, I would like to get any advice about my multiple inheritance. In 95% of cases I try to avoid it, but here it is very effective in the programming point of view. So I would appreciate it if you provide me with your point of view about the architecture of the classes hierarchy - does it even suppose to work as I expect it?

If on this level everything is quite fine, then it would be great if someone gets any idea why doesn't it work.

I have some ideas about workaround, but I really would like to solve this problem (just in order not to repeat the same error anymore).

2
Well, for some unknown reasons gdb frontend of Qt Creator shows me information I want to see, but it is wrong. I've just tested with good old console gdb and here is what I've got: 1) When ExtendedPolygon is created, it has address 0x8416e0 2) item in mousePressEvent points at 0x841700 - QGraphicsItem part of ExtendedPolygon starts with some offset from the beggining of the object 3) obj is casted to B2DObject*, and address is not moved to 0x8416e0 (as QtCreator shows me), but still remains at 0x841700. That's why I get corrupted data.Admiral Larimda
So it looks like I'm using multiple inheritance and casting in a wrong way. It would be great to get any ideas how to modify my basic classes hierarchy in order to make it work more or less similar to my initial idea.Admiral Larimda
Use dynamic_cast instead of reinterpret_cast.divanov
divanov, dynamic_cast in this case is not possible. I could use it in case I would want to convert QGraphicsItem* to ExtendedPolygon*. However in general case I don't know which successor of QGraphicsItem is received after calling itemAt (...).Admiral Larimda

2 Answers

1
votes

Looks like I've found the root cause of my problem. It was just lack of knowledge regarding how multiple inheritance really works on data layer.

Let's assume that we have 2 basic classes, A and B. Each of them provides some internal data fields and some interfaces.

Then we create a derived class AABB, inheriting both A and B:

class AABB : public A, public B {...}

AABB could add some additional data fields and reimplement some of the interfaces, but it is not necessary.

Let's create and object of class AABB:

AABB* obj = new AABB ();

For example, obj points at address 0x8416e0. At this address starts data from ancestor class A. Data from ancestor class B starts with some offset (it should bw equal to sizeof (A)), for example, at 0x841700.

If we have some function f (B* b), and if we pass a pointer at AABB object to that function (like this: f (obj), obj is created above), actually not obj start address is passed, but rather a pointer at a start of B data section of AABB object.

Thus this misunderstanding of multiple inheritance inner works has led me to the problem I've got.

0
votes

I guess Qobjects and multiple inheritance has been already treated. As an example: QObject Multiple Inheritance