PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Ärger mit Mehrfachvererbung in C++



axeljaeger
21-03-2004, 15:31
Ich hab heute ein bißchen Ärger mit Mehrfachvererbung gehabt: Meiner Meinung nach sollte folgendes Programm "Item::start" ausgeben, es gibt aber "Item::draw" aus:


#include <iostream>

using namespace std;

class Item {
public:
virtual void start() { cerr << "Start" << endl; }
};

class Sprite {
public:
virtual void draw() { cerr << "Draw" << endl; }
};

class Object : public Sprite, public Item {
public:
void start() { cerr << "Item::start" << endl; }
void draw() { cerr << "Item::draw" << endl; }
};

int main(int argc, char** argv) {
Object* obj = new Object();
Sprite* spr = (Sprite*)obj;
Item* itm = (Item*)spr;
itm->start();
}


Ich nehme mal an, dass das nicht vorgesehen ist und mir das Programm eigentlich mit einem Segfault abschmieren. In Java darf man meiner Meinung nach sowas machen und in C++ sollte es auch gehen, da ja die Adresse des Pointers die gleiche bleibt. Wenn man von obj direkt auf itm castet, geht es auch.

wraith
21-03-2004, 16:34
Original geschrieben von axeljaeger
und in C++ sollte es auch gehen, da ja die Adresse des Pointers die gleiche bleibt.
Der richtige Cast in diesem Fall ist dynamic_cast (zur Laufzeit), während der C-Style Cast in diesem Fall einem static_cast (zur Compilezeit) entspricht.



Object* obj = new Object();
Sprite* spr = obj;
if(Item* itm = dynamic_cast<Item*>(spr))
{
cout << static_cast<void*>(itm) << endl;
itm->start();
}
Item* itm = (Item*)spr;
cout << static_cast<void*>(itm) << endl;
itm->start();

axeljaeger
22-03-2004, 13:48
ich muss also dynamic_cast verwenden, aber woher weis man das und was genau läuft da schief?

wraith
22-03-2004, 18:12
Original geschrieben von axeljaeger
ich muss also dynamic_cast verwenden, aber woher weis man das
Der 'Trick' ist nie C Casts in C++ zuwenden (siehe dazu Rest des Textes).
Wenn man sich daran hält bekommt man vom Compiler schon die entsprechende Fehlermeldung vorgesetzt.


und was genau läuft da schief?
Das hängt damit zusammen,wie virtuelle Funktionsaufrufe in C++ implementiert sind.
Eine Klasse,die direkt von zwei Klassen mit vtable ableitet, hat nicht nur eine vtable, sondern zwei (entsprechend für n verschiedene direkte Oberklassen).
Das Speicherlayout in diesem Bsp wäre etwa


------------
| Sprite |
| ---> Zeiger auf vtable von Sprite
------------
| Item |
| ---> Zeiger auf vtable von Item
------------
| Object |
|
------------



Object* obj = new Object();
Sprite* spr = obj;
Item* item = obj;//<-- neu hinzugefügt

Bei der Zuweisung obj an spr muß der this-Pointer angepasst werden, damit er auf das Sprite Subobjekt zeigt (glücklicherweise ist in diesem Fall für den Compiler nichts zu tun, Sprite ist 1.Oberklasse von Objekt und damit sind die Adressen bereits korrekt).
Nächster Fall, obj an item, jetzt muß zur Compilezeit Code erzeugt werden, der den this-Pointer anpasst, damit er auf das Item Subobjekt zeigt.
Der Code vom Compiler könnte etwa so aussehen:
Item* item = obj + sizeof(Sprite);
(lass' dir die Adressen von obj,spr und item einmal ausgeben)
Wichtig ist diese Verschiebung für den Zugriff auf nicht virtuelle Funktionen/oder Member von item.
Nun zu


Item* itm = (Item*)spr;

Was haben Item und Sprite gemeinsam?Im Grunde doch nichts, es sind zwei völlig unabhängige Klassen, das sie beide Oberklasse von Objekt sind spielt hier jetzt keine Rolle.Der Compiler sieht nur einen Sprite Pointer (rechts) und einen Item Pointer (links).
[das bringt mich auch jetzt gerade dazu, daß der C Cast in diesem Fall kein static_cast ist (weil Cast zwischen zwei völlig verschiedenen Pointer), sondern ein reinterpret_cast,was gleich wieder ein gutes Argument gegen C Casts in C++ ist :),mit static_cast wäre das nicht passiert (es hätte sich nicht kompilieren lassen)].
Er kann zur Compilezeit den this Zeiger nicht anpassen, erst zur Laufzeit weiß er, daß spr in Wirklichkeit auf ein Objekt zeigt.

axeljaeger
22-03-2004, 20:19
Ich hatte BISHER immer den C-Style-cast verwendet, aber hauptsächlich, weil ich keine befriedigende Erklärung über die C++-Castingoperatoren gesehen hab. Vielleicht sollte ich jetzt damit aufräumen. Hat jmd. einen guten Link zu einer kurzen Erklärung über das casting? Meine C++-Bücher schweigen sich da aus.

wraith
22-03-2004, 20:47
Original geschrieben von axeljaeger
IHat jmd. einen guten Link zu einer kurzen Erklärung über das casting? Meine C++-Bücher schweigen sich da aus.
Du brauchst bessere Bücher ;).
Na hier eine kurze Erläuterung von Dietmar Kuehl
Klick (http://www.google.de/groups?selm=8v46k4%247v9%242%40news.BelWue.DE&oe=UTF-8&output=gplain)
Gibt sicherlich noch ausführliche Erklärungen.

panzi
23-03-2004, 09:52
Original geschrieben von wraith
Du brauchst bessere Bücher ;).
Na hier eine kurze Erläuterung von Dietmar Kuehl
Klick (http://www.google.de/groups?selm=8v46k4%247v9%242%40news.BelWue.DE&oe=UTF-8&output=gplain)
Gibt sicherlich noch ausführliche Erklärungen.

Oder in "C++ in a Nutshell" wird fast alles zu C++ (und auch C!) erklärt. (Nur die Beschreibung zu throw() hinter Funktionsdeklaratioenen hab ich dort nicht gfunden, wie ich in einen anderen Thread erwähnt hab.)
Jedenfalls die C++ casts werden dort auch recht gut erklärt.

axeljaeger
23-03-2004, 15:21
Zu den Büchern: Ich habe hier "C++ echt einfach", das finde ich für seine 300 Seiten richtig gut und "C++ Grundlagen" von Data Becker. Das finde ich für seine 1000 Seiten ziemlich schlecht, von STL wird da nichts erwähnt und diese cast-operatoren fehlen auch. Ich kann also vom Kauf dieses Buches nur abraten.

peschmae
23-03-2004, 15:57
Ich hab den Stroustroup - The C++ Programming Language. Recht nett aber ein Brocken der nicht leicht zu verdauen ist :rolleyes:
Aber so ist C++ wohl halt. Die C++-Cast Dinger sind dort auf jeden Fall auch drin und erläutert :)

MfG Peschmä