error: cannot convert from pointer to base class ‘QGraphicsItem’ to pointer to derived class ‘MyBaseClass’ because the base is virtual Ich hab nen Problem dabei die Fehlermeldung zu verstehen. Was ist damit gemeint? class MyBaseClass : public QObject, virtual public QGraphicsItem { Q_OBJECT [...] } class Something : public MyBaseClass { Q_OBJECT [...] } Was ich (glaube) soweit verstanden zu haben: Ich will hier bei MyBaseClass virtual benutzen, damit ich das Diamond-Problem umgehen kann. Die Q_OBJECT Macros brauche ich damit die connect/Signal/Slot Kommunikation funktioniert. Nur woran hängt das jetzt?
Wovon erbt QGraphicsItem denn? Ich habe gerade keine Qt-Installation hier, um mir die Header anzuschauen. Die Dokumentation im WWW lässt darauf schließen, dass QGraphicsItem nicht von QObject erbt. Dann bräuchte es die virtuelle Vererbung nicht. merciless
Alternativ kannst Du dir auch einmal QGraphicsObject als Basis angucken, das erbt von QObject und QGraphicsItem, dann musst Du dir darüber keine Gedanken mehr machen. http://doc.qt.io/qt-5/qgraphicsobject.html
Danke für Eure Antworten. Ja, Dirk, da hast Recht, ein QGraphicsItem erbt nicht von QObject. Deswegen ja auch die Mehrfachvererbung (ist das hier der richtige Begriff dafür?). Der Hinweis von Dir, blablup, ist auch gut, daran hatte ich nicht gedacht, ich muss mal schauen, ob es noch "Nebenwirkungen" hat, sonst ist es vielleicht wirklich die beste Lösung meine Basisklasse als QGraphicsObject anzulegen. Mir gehts aber garnicht so sehr darum, das aktuelle Problem zu lösen, das hab ich irgendwie heute Nacht per Zufall doch noch geschafft. Aber bitte fragt mich nicht warum es jetzt geht. Ich hab solange die Reihenfolge und Keyworte in den beteiligten Klassen verändert bis es funktioniert hat. Nach einigem Rumspielen gehe ich davon aus, dass es nur daran liegt, dass ich das virtual in der Basisklasse rausgenommen habe. Ob ich mir damit an anderer Stelle etwas kaputtgemacht hat, kann ich noch nicht absehen. Ich würde viel lieber verstehen, was das grundlegende Problem ist. Wann muss ich virtual benutzen, welche Vorteile bringt es, und wie gehe ich damit um wenn der Compiler sagt "Nein". Also zurück zum abstrakten Problem: Was mache ich wenn ich so eine Meldung bekomme? error: cannot convert from pointer to base class ‘QGraphicsItem’ to pointer to derived class ‘MyBaseClass’ because the base is virtual Angenommen ich möchte am Ende Objecte A, B und C haben. Alle sollen Methode x() und y() haben, nur B und C z(); x() ist für alle abgeleiteten Klassen gleich, y() hängt aber von der jeweiligen Klasse ab. Alle brauchen Q_OBJECT Eigenschaften und sollen ebenfalls QGraphicsItems sein (diese Anforderung würde wahrscheinlich durch QGraphicsObject gelöst, aber ignorieren die Existenz dieser Klasse mal für das Gedankenspiel). Aus meiner Anfängersicht hätte ich jetzt gesagt: es gibt eine Basisklasse: MyBase, diese erbt von QOBJECT und virtuell von QGraphicsItem, x() ist im MyBase definiert, y() wird als virtual angegeben. weiterhin gibt Klassen A,B,C die dann nur noch von MyBase erben müssen. y() wird dann jeweils in A,B,C implementiert. Wo liegt mein Verständnisfehler? Warum will der Compiler hier nicht mehr casten können?
Ich würde sagen, Dein Problem hat nichts mit QT zu tun, sondern mit der virtuellen Vererbung. Bei einer normalen (einfachen) Vererbung liegen die Attribute der abgeleiteten Klasse im Speicher unmittelbar hinter denen der Basisklasse. Die Basisklasse wird – vereinfacht gesagt – einfach nach hinten erweitert. Der Anfang des Speicherbereiches ist für abgeleitete und Basisklasse gleich, daher kannst Du problemlos zwischen beiden hin und her casten. (Du kannst! Ob es sinnvoll ist, ist eine andere Frage, die vom konkreten Anwendungsfall abhängt.) Bei Mehrfachvererbung liegen am Anfang des Speicherbereiches der abgeleiteten Klasse die Instanzen mehrerer Basisklassen. Hier sind der Anfang des Speicherbereiches von Basisklasse und abgeleiteter Klasse nur noch für eine Basisklasse gleich. Da aber der Offset zu den anderen Basisklassen dem Compiler bekannt ist, kann er auch hier zwischen den Zeigern auf abgeleitete und Basisklasse (egal welcher) hin und her rechnen. Virtuelle Vererbung macht nun überhaupt erst einmal nur bei Mehrfachvererbung über mindestens zwei Ebenen hinweg Sinn und nur dann, wenn sich mehrere unmittelbare oder mittelbare Basisklassen eine gemeinsame Instanz einer mittelbaren Basisklasse teilen sollen. Beispiel:
1 | struct A {}; |
2 | struct B: virtual public A {} |
3 | struct C: virtual public A {} |
4 | struct D: public B, public C {}; |
Hier teilen sich B und C eine gemeinsame Instanz von A. Damit das funktioniert enthalten B und C meines Wissens nach lediglich eine Referenz auf A, also einen Zeiger, nicht die Attribute von A selbst. Wenn Du nur einen Zeiger auf A hast kannst Du natürlich nicht auf die Adresse schließen, wo dieser gespeichert ist, folglich auch nicht auf die Adressen von B oder C. Das ist schon deshalb nicht möglich, weil es potentiell mehrdeutig wäre. Stell Dir noch folgende Klasse vor:
1 | struct E: public B, public D {}; |
In diesem Fall liegen im Speicher (wahrscheinlich) eine Instanz von B (unmittelbare Basisklasse von E), dann noch eine Instanz von B (mittelbare Basisklasse von E durch D), dann eine Instanz von C (mittelbare Basisklasse von E durch D), die Attribute von D, schließlich die Attribute von E. Irgendwo davor oder dahinter folgt noch die gemeinsame Instanz von A. Auf welches B sollte der Compiler jetzt casten, selbst wenn er die Offsets kennt? Und wenn Du auf E casten möchtest, ist die konkrete Instanz, die Du vor Dir hast, wirklich eine Instanz von E, oder vielleicht eine Instanz von F oder G, die ihrerseits von E erben, was die Offsets (möglicherweise) verschieben würde? Das alles kannst Du nur aus dem Zeiger auf A nicht herleiten, daher ist ein Casten vom Zeiger einer virtuelle Basisklasse auf eine abgeleitete Klasse grundsätzlich nicht möglich. Viel Text, aber ich hoffe das hilft.
:
Bearbeitet durch User
Viel Text, hilft nicht, besonders einem Anfänger. Auch beinhaltet der Text falsche Informationen. ZB: "Der Anfang des Speicherbereiches ist für abgeleitete und Basisklasse gleich, daher kannst Du problemlos zwischen beiden hin und her casten" Eben nicht "problemlos". In machen Fällen kann man casten, in manchen nicht. Man kann beispielsweise nicht eine Instanz der Basisklasse zur abgeleiteten Klasse (down)casten.
BobbyX schrieb: > Eben nicht "problemlos". In machen Fällen kann man casten, in manchen > nicht. Man kann beispielsweise nicht eine Instanz der Basisklasse zur > abgeleiteten Klasse (down)casten. Es geht hier nicht um das Casten von Instanzen, sondern um das Casten von Pointern. Und natürlich sollte ich einen Pointer nur dann (statisch) auf den Typ einer abgeleiteten Klasse casten, wenn ich mir absolut sicher bin, dass es sich tatsächlich um einen Pointer auf eine Instanz dieser Klasse handelt. Die Betonung liegt allerdings au sollte. Können tue ich es in jedem Fall.
Willy schrieb: > Wo liegt mein Verständnisfehler? Warum will der Compiler hier nicht mehr > casten können? Ha, das genaue technische Problem kann ich dir auch nicht erklären, weil ich das immer eine Ebene höher gelöst habe: Deadly Diamond vermeiden. Kann dir auch nur den Tipp geben, ein gutes Buch über OOP mit C++ zu lesen. Man muss nicht immer vererben, nur weil das technisch irgendwie möglich ist ;) merciless
A. H. schrieb: > BobbyX schrieb: >> Eben nicht "problemlos". In machen Fällen kann man casten, in manchen >> nicht. Man kann beispielsweise nicht eine Instanz der Basisklasse zur >> abgeleiteten Klasse (down)casten. > > Es geht hier nicht um das Casten von Instanzen, sondern um das Casten > von Pointern. Und natürlich sollte ich einen Pointer nur dann (statisch) > auf den Typ einer abgeleiteten Klasse casten, wenn ich mir absolut > sicher bin, dass es sich tatsächlich um einen Pointer auf eine Instanz > dieser Klasse handelt. Die Betonung liegt allerdings au sollte. > Können tue ich es in jedem Fall. Meine Aussage gilt sowohl für das Casten von Pointern als auch für die Typumwandlung von Instanzen. Bezüglich des Falles, den du beschrieben hast: Nix "sollte", nix "könnte". Um zu überprüfen ob ein Pointer auf eine Instanz eines bestimmten (abgeleiteten) Klassentypes zeigt benutzt man einen dynamic_cast. static_cast benutzt man hier nur auf systemen ohne RTTI oder wenn man besonders auf Performance achten will. Aber es ging um deine Aussage, dass man "problemlos hin und her casten kann". Das ist schlicht und einfach falsch.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.