Konvertierung in Basisklassen-Objekt



  • Hi,

    ich habe ein kleines Problem mit der impliziten Umwandlung eines Objekts einer abgeleiteten Klasse in ein Objekt der Basisklasse. Das ganze bezieht sich auf MFC-Klassen, aber ich denke, die eigentliche Frage ist hier möglicherweise besser aufgehoben.

    Ich habe eine Klasse CBitmapEx von CBitmap abgeleitet, die ich gegenüber CBitmap um ein paar Methoden ergänzt habe.

    In folgender Zeile bekomme ich dann die Fehlermeldung:
    "error C2662: 'CBitmap::GetBitmap' : cannot convert 'this' pointer from 'const CBitmapEx' to 'CBitmap &'"

    bRetVal = myObj->GetEmblem().GetBitmap(&bmpstruct);
    

    Wobei die Methode GetEmblem "const CBitmapEx&" zurückgibt und die Methode GetBitmap() (wie die Fehlermeldung schon sagt) zu CBitmap gehört.

    Versuche ich es stattdessen mit einem cast auf CBitmap durchsucht der Compiler die Basisklassen nach einem passenden Kopierkonstruktor und meckert, dass er keinen findet bzw. dass dieser in CObject private ist (CBitmpap stellt keinen bereit).

    Ein Aufruf von GetBitmap über einen CBitmapEx-Zeiger funktioniert an anderer Stelle problemlos.

    1.)
    Ich hätte jetzt eigentlich erwartet, dass automatisch die GetBitmap()-Methode der Basisklasse aufgerufen wird und zwar unabhängig davon, ob der Aufruf über ein CBitmapEx-Objekt oder einen CBitmapEx-Zeiger erfolgt. Warum ist das nicht der Fall?

    2.)
    Da die Klasse CBitmap (genauso wie ihre Basisklassen) keinen Kopierkonstruktor bereitstellen hilft mir ein cast in diesem Fall auch gar nicht weiter, oder? Oder würde ein Kopierkonstruktor in meiner abgeleiteten Klasse CBitmapEx helfen?

    3.)
    Wie sollte ich sinnvollerweise in diesem Fall vorgehen, wenn ich die Methode GetEmblem (gehört zu einer anderen Klasse, s.o.) nicht dahingehend ändern will, dass sie ein CBitmapEx* zurückgibt?

    Ich hoffe, ich habe das einigermaßen verständlich formuliert.

    Danke schonmal vorab!



  • Dein Problem hat nichts direkt mit der Vererbung zu tun, sondern mit const-correctness. Die Methode CBitmap::GetBitmap() ist nicht konstant und kann darum auch nicht über einen 'const CBitmapEx&' aufgerufen werden. Als Lösung kannst du entweder deine GetEmblem() soweit ändern, daß sie eine nicht-konstante Referenz (oder ein Objekt per Kopie) zurückgibt oder einen const_cast<> verwenden.



  • 1)Kann es sein, dass die Methode GetBitmap() keine const Methode ist? Du darfst auf eine const referenz keine Methode anwenden, die diese Referenz veraendern koennte...

    2)Ein Kopierkonstruktor koennte evtl. helfen, wenn er das zu kopierende Objekt const laesst:

    CBitmapEx::CBitmapEx(const CBitmapEx& other) {}...
    
    //...
    
    CBitmapEx tmpBitMapEx(myObj->GetEmblem());
    bRetVal = tmpBitMapEx.GetBitmap(&bmpstruct);
    

    ein const_cast ginge vermutlich auch, ist in meinen Augen aber etwas "schmutzig" 😉



  • Oje, in die komplett falsche Richtung gedacht...

    Da ich an der Klasse, die GetEmblem() bereitstellt eigentlich nichts mehr ändern möchte, werde ich es mal über den Kopierkonstruktor probieren. Sollte ich daran scheitern, muss es halt der "schmutzige" Weg sein 😉

    Danke für die Antworten!



  • Nanuq schrieb:

    Oje, in die komplett falsche Richtung gedacht...

    Da ich an der Klasse, die GetEmblem() bereitstellt eigentlich nichts mehr ändern möchte, werde ich es mal über den Kopierkonstruktor probieren. Sollte ich daran scheitern, muss es halt der "schmutzige" Weg sein 😉

    Danke für die Antworten!

    Na - ich wüsste nicht, wie das scheitern sollte. Und wo doch, wüsste ich nicht, warum "der schmutzige Weg" dann funktionieren sollte. 😉

    Gruß,

    Simon2.



  • Möglicherweise stelle ich mich extrem blöd an, aber ich komme tatsächlich mit den beiden von euch angegebenen Lösungsmöglichkeiten nicht weiter.

    1.) Kopierkonstruktor für CBitmapEx
    Wie würde denn ein Kopierkonstruktor in diesem Fall aussehen?
    Normalerweise würde ich das so machen:

    CBitmapEx::CBitmapEx(const CBitmapEx& other)
     : CBitmap(other) // Initialisierung des Basisklassen-Subobjekts
    {
        // zusätzliche Member kopieren: entfällt hier
    }
    

    Wie schon gesagt, gibt es allerdings in CBitmap (und den anderen "Oberklassen") keinen Kopierkonstruktor, den ich in der Initialisierungsliste aufrufen könnte. (Weitere Membervariablen hat meine abgeleitete Klasse nicht).

    2.) const_cast<>
    Ich muss zugeben, dass ich mich nicht erinnern kann, den const_cast jemals verwendet zu haben. Aber wenn ich es richtig verstanden habe, müsste es sich damit doch folgendermaßen verhalten:
    Meine Methode

    GetEmblem()
    

    liefert const CBitmapEx&

    const_cast<CBitmapEx> (myObj->GetEmblem())
    

    sollte dann also ein CBitmapEx sein, da das const Attribut entfernt wurde.
    Demzufolge sollte die komplette Zeile dann so aussehen:

    bRetVal = const_cast<CBitmapEx>(myObj->GetEmblem()).GetBitmap(&bmpstruct);
    

    Mein Compiler meint dazu:
    error C2440: 'const_cast' : cannot convert from 'const CBitmapEx' to 'CBitmapEx'
    Conversion requires a constructor or user-defined-conversion operator, which can't be used by const_cast or reinterpret_cast

    Und wie ein solcher conversion-operator aussehen sollte, weiß ich dann auch wieder nicht...:(

    Ich bin versucht, das ganze ein extrem schlechtes Design dieser MFC-Klasse zu nennen, aber ich fange vorsichtshalber mal mit der Suche bei mir selbst an ;).

    Sollte mir hier allerdings jemand zustimmen, werde ich die Frage vielleicht noch einmal im MFC-Forum posten und hoffen, dass ich über einige MFC-Umwege vielleicht doch zum Ziel komme 😕


  • Mod

    Nanuq schrieb:

    aber ich fange vorsichtshalber mal mit der Suche bei mir selbst an ;).

    Ein gute Idee. const_cast kann nur für Pointer- und Referenz-casts benutzt werden. Mithin

    bRetVal = const_cast<CBitmapEx&>(myObj->GetEmblem()).GetBitmap(&bmpstruct);
    

    Das impliziert, dass const_cast niemals einen copy-ctor aufruft - was aber nichts mit dem Design der Klasse zu tun hat.



  • Tatsache, jetzt ist mein Compiler einverstanden :), danke!

    Mit dem schlechten Design meinte ich allerdings eigentlich die Klasse CBitmap, weil sie keinen Kopierkonstruktor bereitstellt...

    pumuckl schrieb:

    ein const_cast ginge vermutlich auch, ist in meinen Augen aber etwas "schmutzig" 😉

    ...und deswegen würde mich natürlich nach wie vor interessieren, ob bzw. wie in diesem Fall die Variante mit dem copy-ctor funktionieren würde.
    Weil ich es schon auch ein wenig unschön finde eine Referenz auf const zurückzugeben und nachher das const durch den cast wieder aufzuheben.

    CStoll schrieb:

    Als Lösung kannst du entweder deine GetEmblem() soweit ändern, daß sie eine nicht-konstante Referenz (oder ein Objekt per Kopie) zurückgibt

    Und ich meine mich zu erinnern irgendwo gelesen zu haben, dass man Objekte (d.h. keine Standarddatentypen) in Get-Methoden normalerweise per const & zurückgeben sollte (Kopie sparen)...



  • Da ich an der Klasse, die GetEmblem() bereitstellt eigentlich nichts mehr ändern möchte

    Der einzig saubere Weg wäre IMHO CBitmap::GetBitmap const zu machen. Blöderweise hat MS das bis jetzt nicht gemacht... *g*

    GetEmblem stattdessen zu ändern dass es einen non-const Referenz zurückgibt halte ich für einen Fehler, wieso auch, das Problem liegt ja bei CBitmap (eine Funktion die ganz klar KEIN Mutator ist ist nicht const -> Fehler, pöse).

    Ich würde hier also wahrscheinlich auch nen const_cast machen, und nen Kommentar dazuschreiben. CBitmap::GetBitmap verwendet intern bloss ::GetObject(), und mir wäre nicht bekannt dass ::GetObject an dem übergebenen Objekt was ändern darf.


  • Mod

    Sinnvoll wäre, GetBitmap zu überladen (als const Funktion) und den const_cast dort zu verstecken.



  • hustbaer schrieb:

    Der einzig saubere Weg wäre IMHO CBitmap::GetBitmap const zu machen. Blöderweise hat MS das bis jetzt nicht gemacht... *g*

    GetEmblem stattdessen zu ändern dass es einen non-const Referenz zurückgibt halte ich für einen Fehler, wieso auch, das Problem liegt ja bei CBitmap (eine Funktion die ganz klar KEIN Mutator ist ist nicht const -> Fehler, pöse).

    Na also! Auch wenn es mir eigentlich nicht weiterhilft, ist das doch genau das, was ich hören wollte 😃 -> schlechtes CBitmap-Design

    camper schrieb:

    Sinnvoll wäre, GetBitmap zu überladen (als const Funktion) und den const_cast dort zu verstecken.

    Dann sollte ich mir allerdings absolut sicher sein, dass CBitmap::GetBitmap() an dem übergebenen Objekt wirklich nichts ändert...
    Werde noch einmal zusehen, dass ich das sicherstellen kann.



  • camper schrieb:

    Sinnvoll wäre, GetBitmap zu überladen (als const Funktion) und den const_cast dort zu verstecken.

    Inzwischen habe ich das so umgesetzt.

    int CBitmapEx::GetBitmap(BITMAP* pBitMap) const
    {
    	return const_cast<CBitmapEx&>(*this).CBitmap::GetBitmap(pBitMap);
    }
    

    Da mich der Aufruf der Basisklassenmethode über den Punktoperator aber ein wenig irritiert (eigentlich irritiert mich der ganze Ausdruck ;)), wollte ich noch einmal absichern, ob das so korrekt ist.


Anmelden zum Antworten