std::unique_ptr: Base -> Derived casten



  • Hallo,

    ich habe eine Frage zu der Konvertierung von einem unique_ptr (Basiklasse) nach abgeleitet Klasse.

    Es geht darum das ich die Basisklasse FooBase habe und die abgeleitete Klasse FooDerived.

    In einer Map speichere ich

    std::map<int, std::unique_ptr<FooBase>>
    

    Nun möchte ich den Zeiger natürlich bei Bedarf in einen Zeiger vom Typ

    std::unique_ptr<FooDerived>>[/
    

    casten.

    Welche Vorgehensweise ist hier die Beste. Ich kann natürlich get() aufrufen und einen rohe Zeiger erhalten.

    Interessanterweise führt der Compiler bei std::for_each() und dem Argument std::unique_ptr<FooDerived> automatisch eine Konvertierung durch.


  • Mod

    thomas5_33 schrieb:

    Nun möchte ich den Zeiger natürlich bei Bedarf in einen Zeiger vom Typ

    std::unique_ptr<FooDerived>>[/
    

    casten.

    Das finde ich überhaupt nicht natürlich. Wozu meinst du das zu benötigen? Das ist eigentlich ein recht klares Zeichen, dass dein Design fehlerhaft ist.



  • Es gibt einen bestimmten Fall, wo ich Methoden der abgeleiteten Klasse brauche.


  • Mod

    thomas5_33 schrieb:

    Es gibt einen bestimmten Fall, wo ich Methoden der abgeleiteten Klasse brauche.

    Das ist mir schon klar, dass du sicherlich irgendwas aus der abgeleiteten Klasse brauchen wirst, sonst würdest du nicht fragen. Und ich sage dir, wenn das so ist, dann ist der eigentlich Fehler im Design deiner Klassen. Da fände ich es ungünstig, dir zu erklären, wie du um den Fehler drumherum hacken kannst. Besser wäre eine detaillierte Erklärung deinerseits (also nicht nur das offensichtliche nennen), wieso du das brauchst. Dann könnte man dir wirklich helfen.



  • mit .release() bekommst du den Besitz über den Zeiger zurück (d.h. der alte unique_ptr ruft kein delete auf.)
    Den Zeiger kannst du casten und in einen neuen unique_ptr reinstopfen.

    static_pointer_cast gibt es leider nur für shared_ptr.



  • SeppJ schrieb:

    thomas5_33 schrieb:

    Es gibt einen bestimmten Fall, wo ich Methoden der abgeleiteten Klasse brauche.

    Das ist mir schon klar, dass du sicherlich irgendwas aus der abgeleiteten Klasse brauchen wirst, sonst würdest du nicht fragen. Und ich sage dir, wenn das so ist, dann ist der eigentlich Fehler im Design deiner Klassen. Da fände ich es ungünstig, dir zu erklären, wie du um den Fehler drumherum hacken kannst. Besser wäre eine detaillierte Erklärung deinerseits (also nicht nur das offensichtliche nennen), wieso du das brauchst. Dann könnte man dir wirklich helfen.

    Das ist mir bewusst. Ich denke auch aktuell über eine Designänderung nach, aber die goldene Lösung ist mir noch nicht eingefallen.



  • Unter der Annahme, dass der neue Zeiger nicht besitzend sein soll:

    FooDerived *non_owning_ptr = dynamic_cast<FooDerived*>(owner.get());
    

    Andernfalls würde ich über den Umstieg auf std::shared_ptr nachdenken, weil die Besitzverhältnisse mit einer unique_ptr-Konstruktion schwer überschaubar werden (was, wenn der Cast fehlschlägt, beispielsweise?). Dann gibt es std::dynamic_pointer_cast:

    std::shared_ptr<FooDerived> new_ptr = std::dynamic_pointer_cast<FooDerived>(old_ptr);
    

    Gleichwohl: einen Downcast zu wollen, ist, wie schon erwähnt wurde, ein Symptom eines kaputten Designs. Wenn du mit dem Interface von FooBase nicht auskommst, solltest du entweder deine Zeiger so umstricken, dass sie auf das zeigen, was du wirklich brauchst, oder das Interface von FooBase entsprechend erweitern.



  • thomas5_33 schrieb:

    std::map<int, std::unique_ptr<FooBase>>
    

    Nun möchte ich den Zeiger natürlich bei Bedarf in einen Zeiger vom Typ

    std::unique_ptr<FooDerived>>[/
    

    Ist dir das "unique" im Namen nicht aufgefallen? Oder weisst du nur nicht was es bedeuten soll?
    Ich meine ... wie soll das gehen?

    Ein "Cast" zerstört/verändert ja wohl nie den Ausgangswert. D.h. es müsste danach zwei "unique" Pointer geben die beide auf das selbe Objekt zeigen (dass die nen unterschiedlichen Typ haben ist dabei ja nicht relevant).
    D.h. sie sind nimmer "unique".

    => Kann nicht gehen.

    Und das ganz abgesehen davon ob es jetzt ein Designfehler ist oder nicht.

    => Caste den rohen Zeiger. => Problem gelöst.



  • thomas5_33 schrieb:

    Das ist mir bewusst. Ich denke auch aktuell über eine Designänderung nach, aber die goldene Lösung ist mir noch nicht eingefallen.

    Virtuelle Funktionen.

    seldon schrieb:

    Gleichwohl: einen Downcast zu wollen, ist, wie schon erwähnt wurde, ein Symptom eines kaputten Designs.

    Erst recht dynamische Downcasts. Es gibt schon sinnvolle Situationen, wo man tatsächlich downcasten muss, aber dann kennt man fast immer den abgeleiteten Typ und kann static_cast einsetzen.

    seldon schrieb:

    Andernfalls würde ich über den Umstieg auf std::shared_ptr nachdenken, weil die Besitzverhältnisse mit einer unique_ptr-Konstruktion schwer überschaubar werden

    Ne, Besitzverhältnisse sind mit unique_ptr am überschaubarsten: Genau ein Zeiger hält das Objekt. Simpler gehts nicht.

    shared_ptr sollte man nur einsetzen, wenn man ihn wirklich braucht. Ist leider auch ein häufiger Designfehler...


Log in to reply