static_cast<> für upcast?



  • Das ist ein Downcast..

    Und warum willst du dynamic_cast nicht verwenden? Der ist ja genau für das da und mit dem kannst du auch prüfen, ob die Konvertierung geklappt hat.

    Im übrigen solltest du vlt. nochmal darüber nachdenken, ob der Downcast überhaupt nötig ist..



  • drakon schrieb:

    Und warum willst du dynamic_cast nicht verwenden? Der ist ja genau für das da und mit dem kannst du auch prüfen, ob die Konvertierung geklappt hat.

    Ohne RTTI ist kein dynamic_cast möglich, da muss man sicher sein, dass es geht und einen static_cast machen.



  • Superlexx schrieb:

    drakon schrieb:

    Und warum willst du dynamic_cast nicht verwenden? Der ist ja genau für das da und mit dem kannst du auch prüfen, ob die Konvertierung geklappt hat.

    Ohne RTTI ist kein dynamic_cast möglich, da muss man sicher sein, dass es geht und einen static_cast machen.

    Was willst du mir damit sagen?
    RTTI ist involviert man darf es benutzen und muss es nicht selbst von Hand nachbauen..



  • cast-spaß schrieb:

    und angenommen, ich habe ein objekt, was zu 100% ein "derived" ist, sowie ein "base" pointer darauf:
    [...]
    darf ich dann davon ausgehen, dass ein "static_cast<derived *>" ein gültigen
    zeiger zurückliefert?

    Ja.

    cast-spaß schrieb:

    könnte die mehrfachvererbung wegen this-verschiebung problematisch werden?

    Nein, static_cast sorgt in diesem Fall für einen korrekten Downcast. Allerdings nur, solange base keine virtuelle Basisklasse von derived ist. Und du musst dir wirklich sicher sein, ansonsten hast du undefiniertes Verhalten.

    Wenn du Sicherheit im Debug-Modus und Effizienz im Release-Modus erreichen möchtest, kannst du dir einmal boost::polymorphic_downcast anschauen.



  • Nexus schrieb:

    cast-spaß schrieb:

    könnte die mehrfachvererbung wegen this-verschiebung problematisch werden?

    Nein, static_cast sorgt in diesem Fall für einen korrekten Downcast. Allerdings nur, solange base keine virtuelle Basisklasse von derived ist. Und du musst dir wirklich sicher sein, ansonsten hast du undefiniertes Verhalten.

    jo, ich bin mir sicher. mehr als 100% geht doch nicht? 😉

    allerdings ist, base eine virtuelle basisklasse von `derived[/b].

    wenn ein statischer upcast möglich ist, warum dann nicht auch ein statischer

    downcast?

    boost verwendet intern auch nur einen [c]static_cast` .


  • Mod

    cast-spaß schrieb:

    allerdings ist, base eine virtuelle basisklasse von [c]derived[/b]. wenn ein statischer upcast möglich ist, warum dann nicht auch ein statischer downcast?

    Weil die Lage des virtuellen Basisklassensubobjektes relativ zu davon abgeleiteten Subobjekten vom Typ des vollständigen Objektes abhängt. Im Falle eines Basisklassenpointers ist der vollständige Typ aber werder bekannt, noch kann er im Falle nichtpolymorpher Klassen ermittelt werden, und es gibt keine Möglichkeit, dies dem Castoperator mitzuteilen.
    Eine implizite Konvertierung eines Zeigers auf ein Objekt einer abgeleiteten Klasse in einen Zeiger auf das virtuelle Basisklassensubobjekt ist deshalb möglich, weil hierfür zusätzliche Informationen im Objekt gespeichert werden. Als Faustregel kann man davon ausgehen, dass für jede virtuelle Basisklasse, die nicht indirekt vererbt wird, im abgeleiteten Objekt ein zusätzlicher Zeiger existiert, der während der Konstruktion automatisch initialisiert wird.

    Mit ein bisschen Voodoo, kann man den Cast doch noch durchführen, im Zweifelsfall sollte aber eher das Design überdacht werden. Voraussetzung hierfür ist, dass der vollständige Typ des Objektes bekannt ist, und ein Objekt dieses Typs angelegt wurde. Da alle vollständigen Objekte das gleiche Layout haben, können wir ansetzen:

    Derived* virtual_downcast(Base* p)
    {
        static complete_type prototype(...);
        static const std::diffptr_t delta = reinterpret_cast<char*>( &prototype ) - reinterpret_cast<char*>( static_cast<Base*>( &prototype ) );
        return reinterpret_cast<complete_type*>( reinterpret_cast<char*>( p ) + delta );
    }
    

    Sieht böse aus, ist es auch.



  • sowas ähnliches dachte ich mir bereits 😃

    in meinem fall ist aber der vollständige typ von derived
    bekannt.

    struct Interface
    {
        virtual void func() = 0;
    };
    struct SpecialInterface : virtual public Interface
    {
        virtual void foo() = 0;
    };
    /////////////////////////////////////////////////////////
    class Impl : virtual public Interface
    {
    public:
        void func(); // nicht abstrakt, implementationsklasse
    };
    class SpecialImpl : virtual public SpecialInterface, public Impl
    {
    public:
        void foo();
    };
    void do_something(SpecialInterface *ptr)
    {
        // hier ist der vollständige typ von SpecialImpl bekannt.
        // ptr ist zu 100% ein SpecialImpl!
        SpecialImpl *impl = static_cast<SpecialImpl *>(ptr); // erlaubt?
    }
    int main()
    {
        SpecialInterface *ptr = new SpecialImpl;
        do_something(ptr);
        delete ptr;
    }
    

    in diesem A B C D diagramm (b und c erben von a, d von b und c)
    möchte ich nicht von A nach D, sondern von B nach D.
    alle typen sind vollstädnig bekannt.

    afaik muss static_cast evtl beim cast von D nach B den this-pointer
    verändern, je nachdem wo B liegt. dieser offset muss compiletime-konstant
    sein, da die klassen sich nicht ändern.

    jetzt möchte ich nur den umgekehrten weg gehen. B -> D. B ist ein D.
    natürlich könnte man den voodoo-hack nehmen (:D) und einfach den offset abziehen,
    aber kann static_cast das nicht auch?



  • warum benutzt du hier die virtuelle Vererbung? Das Ding kommt eigentlich nie(!) vor. Außer natürlich, du hast ein schlechtes Design (wie zum Beispiel nen kombinierten input/outputstream *hust*)


  • Mod

    cast-spaß schrieb:

    afaik muss static_cast evtl beim cast von D nach B den this-pointer
    verändern, je nachdem wo B liegt. dieser offset muss compiletime-konstant
    sein, da die klassen sich nicht ändern.

    jetzt möchte ich nur den umgekehrten weg gehen. B -> D. B ist ein D.
    natürlich könnte man den voodoo-hack nehmen (:D) und einfach den offset abziehen,
    aber kann static_cast das nicht auch?

    Niemand teilt static_cast den vollständigen Typ mit, also nein. Die einzige Möglichkeit, die mir auf Anhieb einfällt, ist das Offset explizit in den Quellcode zu schreiben und dann bei der Ausführung zu testen, ob der Wert stimmt, oder ggf. beim Konfigurieren des Projekts ein kleines Script auszuführen, dass diesen Wert ermittelt und diesen beim Compilieren weiterzugeben.

    Warum hier virtuelle Vererbung zwischen SpecialInterface und SpecialImpl benutzt wird, erschließt sich mir ebensowenig wie otze.



  • weil ich damit erzwinge, dass die konkrete funktion die abstrakte überschreibt,
    und nicht anderherum. habe ich vor langer zeit herausgefunden, und seitdem
    immer für interfaces genommen.

    ich möchte auch wirklich nur ein objekt der basisklasse.



  • Ahh ich sehs grad...dein Design ist wahrscheinlich ziemlich wirr...

    Das Poblem ist, dass du SpecialImpl von Impl erben lässt...das ist meistens nicht gut.



  • warum sollte das nicht gut sein?

    ich vermeide so das erneute definieren von jeweils 5 funktionen in ~70 klassen.
    das bringt schon etwas.



  • cast-spaß schrieb:

    ich vermeide so das erneute definieren von jeweils 5 funktionen in ~70 klassen.
    das bringt schon etwas.

    Wär das nicht ein Fall von: das gehört ins Interface, weil es eh in allen Klassen gleich ist?

    C++ ist nicht Java.



  • otze schrieb:

    cast-spaß schrieb:

    ich vermeide so das erneute definieren von jeweils 5 funktionen in ~70 klassen.
    das bringt schon etwas.

    Wär das nicht ein Fall von: das gehört ins Interface, weil es eh in allen Klassen gleich ist?

    C++ ist nicht Java.

    java *igitt*

    Ist ja auch alles im Interface, aber die vererbungshierachie der Interfaces
    möchte ich auch in der klassenstruktur nachbilden. d.h.

    klassen | interface
            |
      CA  <-|-  A
       |    |   |
       v    |   v
      CB  <-|-  B
       |    |   |
       v    |   v
      CC  <-|-  C
    

    A, B und C sind die interfaces. ich kenne genug libarys, deren öffendliche
    schnittstelle auf vererbung basiert.

    die einzige gute möglichkeit ist halt die mehrfachvererbung.

    ich hatte es auch schon mal mit class-chaining versucht, via templates.
    aber das war eine katastrophe, sprich _nicht_ schön.



  • ich bins nochmal 😞

    // öffentliches interface
    struct Base
    {
    	virtual void func() = 0;
    };
    struct Interface : virtual public Base //// <-- ES GEHT UM DAS VIRTUAL HIER
    {
    	virtual void specialfunc() = 0;
    };
    //////////// ab hier die implemenatation
    class CBase : virtual public Base
    {
    	void func(){}
    };
    class CInterface : virtual public Interface, public CBase
    {
    	void specialfunc(){}
    };
    
    int main()
    {
    	CInterface *ptr = new CInterface;
    	Interface *iptr = ptr;
    	Base *b = iptr;
    	Interface *iptr2 = static_cast<Interface *>(b); // geht nicht :(
    }
    

    mit dem virtual kann er nicht von Base * zu Interface *
    koonvertieren, ohne virtual überschreibt meine funktion das interface
    nicht, und die funktion ist immer noch abstrakt.

    wie kann ich das umgehen? ab besten ohne solche haXXs, das muss doch gehen.
    ohne dynmaic_cast 🙂

    MfG cast-spaß



  • Nein, virtual macht dir alles kaputt.



  • Warum willst du eigentlich keinen dynamic_cast verwenden?



  • es ist zu langsam.

    grade die msvc-implementierung ist eine katastrophe. irgentwo meine ich
    gelesen zu haben, dass die funktion klasseninfos aus der .exe liest.

    meine jetzige lösung ist eine virtuelle methode in dem interface, void *Downcast()

    der teil ist wirklich performancekritisch und wird tausende male aufgerufen


Anmelden zum Antworten