Zugriff auf Vaterobjekt in überladenem Operator
-
Hallo,
ich habe eine Klasse MeineKlasse.
Diese Klasse hat ein Attribut
MeineKlasse *m_pMeineKlasseParent;Sie hat weiterhin eine Funktion
MeineKlasse *getParent();deren Implementierung so lautet:
MeineKlasse * MeineKlasse ::getParent() { return m_pMeineKlasseParent; }Soweit, so einfach.
Nun möchte ich auf ein Attribut der Vaterklasse zugreifen, um es mit Qt zu serialisieren. Dazu wird folgender Operator überladen.
QDataStream& operator<<(QDataStream &r_QDataStream, const MeineKlasse &r_ MeineKlasse)In seiner Implementierung lege ich mir einen Zeiger an.
MeineKlasse *m_pMeinZeigerZumZugriff = r_ MeineKlasse.getParent();Dies ergibt beim Kompilieren:
Fehler 1 error C2662: 'MeineKlasse::getParent': this-Zeiger kann nicht von 'const MeineKlasse' in 'MeineKlasse &' konvertiert werden.
Kann mir jemand diesen Fehler erklären und korrigieren?
-
In deinem überladenen operator<< besitzt du nur eine const-Referenz auf ein MeineKlasse-Objekt. Du kannst nur auf const- oder statische Methoden der Objekte zugreifen, deren const-Referenz du besitzt.
Aber jetzt lass nicht das const weg, weil es der schmerzloseste Weg ist, sondern füge entsprechende const-Methoden hinzu.Viele Grüße,
Michael
-
Richtig, nach dem Hinzufügen von
MeineKlasse * MeineKlasse::getParent() const { return m_p MeineKlasse; }im Header meiner Klasse funktioniert alles. Vielen Dank für den Ratschlag. S. 244 im deutschen Stroustrup erklärt das Ganze auch noch einmal, wer es genau wissen will.
-
Nun ist natürlich die Frage, ob die const-Methode einen nicht-const-Zeiger auf den Vater zurückgeben sollte... wenn der dann wieder eine nicht-const-Methode hat, um an einen nicht-const-Zeiger auf das Kind heranzukommen, könntest du dir das const immer wegcasten, was ja wohl nicht im Sinne des Erfinders ist.
Viele Grüße,
Michael
-
Das würde konkret heissen, dass das so auszusehen hat, oder?:
const MeineKlasse * MeineKlasse::getParent() const { return m_pMeineKlasse; }Das Attribut ist nun auch konstant definiert.
const MeineKlasse *m_pMeineKlasse;Das führt allerdings zu dem Problem, dass ich keine Attribute der Klasse mehr mit set-Methoden verändern kann.
Fehler 1 error C2662: 'MeineKlasse::setScaleX': this-Zeiger kann nicht von 'const MeineKlasse' in 'MeineKlasse &' konvertiert werden
Das soll aber passieren können. Was muss ich machen?
-
Wenn du in dem "Kind-Objekt" etwas an dem Vater ändern möchtest, darfst du den Zeiger natürlich nicht als const-Zeiger abspeichern. Daher musst du dann selber aufpassen, dass du nur const-Methoden vom Vater aufrufst von einer const-Methode der Kindklasse (falls diese Einschränkung überhaupt sinnvoll ist). C++ hilft Dir hier wenig um const-Korrekt zu bleiben, denn es überprüfut nur, ob das Objekt dessen const-Methode aufgerufen wird "byteweise" konstant bleibt (und du veränderst ja nur das Vaterobjekt, somit ist das gegeben) Du könntest beispielsweise in der Kind-Klasse eine const und eine non-const getParent-Methode implementiert haben und in den Methoden dieser Klasse diese Verwenden anstatt direkt auf den Zeiger zuzugreifen. Damit kannst du dir dann auch sicher sein, nichts zu "verbrechen"

Viele Grüße,
Michael
-
Ich habe jetzt beide Methoden implementiert und nun klappt auch wieder der Zugriff. Der Vater ist bei mir übrigens ein Objekt desselben Typs.
Vielen Dank für Deine Erklärungen. Ich frage mich nur, warum der Rückgabetyp selbst und die Methode jeweils const oder nicht deklariert werden können. Wo ist da noch der Unterschied?
-
Naja, ich verstehe deine Frage nicht so ganz. Du siehst ja selber, dass nicht-const-Operation auf Objekte zu denen man nur const-Zeiger/Referenzen (Oder die an sich const sind) besitzt zu Compiler-Zeit-Fehlern. Durch diese Eigenschaft kannst Du von außen gesehen viel mehr Eigenschaften vom Code ablesen. Z.B. hindert dich so der Compiler daran, Objekte an Stellen zu verändern (selbst wenn man es dem Code direkt nicht ansehen würde, dass überhaupt was verändert wird), an denen du selber das niemals wolltest, es aber aus versehen passiert.
Diese eigenen const-Geschichten sind für das Objekt selbst erstmal nicht besonders interessant (siehe weiter unten). Aber Code der diese Objekte verwendet möchte zB. sicher sein, dass sich das Objekt in bestimmten Aufrufen nicht verändert. Wenn du also eine const-Referenz in irgendeine unübersichtliche Funktion gibst, die wieder millionen unübersichtliche Funktion aufruft, kannst du bei const-korrektem Code sicher sein, dass sich das Objekt nicht verändert hat.
Wenn du aus deinem Objekt, dass ein bestimmtes anderes Objekt verwaltet, const-Referenzen zu letzterem herausgibst, kann es sicher sein, dass der Aufrufer nichts dummes damit anstellt (von dem das herausgebende Objekt nichts mitbekommen würde und dass das Objekt in einem "inkonsistenten" Zustand hinterlassen würde), sofern man darauf vertraut, dass der Aufrufer keine const_cast's macht (was man sowieso tunlichst vermeiden sollte...) Die einzige Ausnahme zu der const_cast-Regel ist für mich, dass man nicht-const Methoden durch const-Methoden implementieren kann, indem man in der nicht-const-Methode entsprechend casted:const parentClass* thisClass::getParent() const { return parent_; } parentClass* thisClass::getParent() { return const_cast<parentClass*>( static_cast<const thisClass*>(this)->getParent() ); }In diesem Beispiel bringt das natürlich noch nichts, aber man kann sich natürlich Fälle vorstellen, wo die Funktionen länger sind und man sich so einiges an dupliziertem Code sparen könnte.
Für alles weitergehende bin ich aber vielleicht auch nicht der richtige Ansprechpartner, schau mal bei den Gurus im C++-Forum hier rein. Dort, wo dieser Thread auch eigentlich hingehört hätte

Viele Grüße,
Michael
-
Die Frage bezog sich mehr darauf, warum es in der Signatur der Methode zweimal das Schlüsselwort const gibt. Ich wollte auf den Unterschied zwischen dem ersten const und dem zweiten const hinaus. Vermutlich wird das erste sich auf den Rückgabetyp beziehen, im Fall einfacher Datentypen einfach die Veränderung derer vermeiden. Bei Objekten wird das dann schon schwieriger, verhindert es da nur das Verändern des Zeigers oder aber des Objekts? Und wozu dient nur das zweite const dann noch?
Auf so wilde Ideen wie das Umcasten wäre ich wahrscheinlich nicht von selbst gekommen

Viele Grüße
-
Das const vor dem Rückgabetyp bezieht sich auch nur auf diesen. Das const hinter dem Funktionsprototypen bezieht sich auf die Funktion (diese Funktion kann nur andere const-Funktionen oder statische Funktionen des Objekts aufrufen und kann die Datenmember nicht verändern... Für die const-Funktion ist der this-Zeiger const).
Da aber die const Methode etwas von dem Objekt herausgibt, sollte dieses etwas auch const sein, ansonsten könnte ein Aufrufer, der nur einen const-Zeiger oder Referenz oder überhaup ein const-Objekt hat, dieses verändern.Viele Grüße,
MichaelPS: mal ein Beispiel, falls du nicht-const herausgibst:
class Some { public: foo* getData() const { return data; } private: foo* data; }; void func( const Some& p ) { p.getData()->x = 5; // Achtung! p geändert obwohl const! }