Designfrage: Mehrfachvererbung / Interfaces



  • Ja, das funktioniert - abgesehen davon, dass man dem shared_ptr nicht den Zeigertyp sondern den Typ, auf den gezeigt werden soll übergibt. Und was intrusive Zeiger angeht, kannst du das in einen intrusive_ptr wrappen, dann hast du intrusives Reference Counting und trotzdem automatisch.



  • @314159265358979
    Dein hier skizzierter shared_ptr ist intrusive, denn man kann nicht mehr einfach Objekte die man von irgendwoher bekommt reinstecken.

    @Baldur
    Ja, man kann ganz normal () casten und Upcasts sind weiterhin implizit. ( man muss nur boost::xxx_pointer_cast<>() statt xxx_cast<>() verwenden)

    Was nicht mehr geht (oha, ein Nachteil von shared_ptr der ja angeblich keine Nachteile hat, na sowas), ist ... wenn du nen rohen Zeiger bekommst kannst du keinen shared_ptr mehr draus machen, d.h. du kannst kein "retain" machen.



  • hustbaer schrieb:

    Was nicht mehr geht (oha, ein Nachteil von shared_ptr der ja angeblich keine Nachteile hat, na sowas), ist ... wenn du nen rohen Zeiger bekommst kannst du keinen shared_ptr mehr draus machen, d.h. du kannst kein "retain" machen.

    _Was genau_ geht hier angeblich mit shared_ptr nicht?



  • 314159265358979 schrieb:

    hustbaer schrieb:

    Was nicht mehr geht (oha, ein Nachteil von shared_ptr der ja angeblich keine Nachteile hat, na sowas), ist ... wenn du nen rohen Zeiger bekommst kannst du keinen shared_ptr mehr draus machen, d.h. du kannst kein "retain" machen.

    _Was genau_ geht hier angeblich mit shared_ptr nicht?

    Du kannst diese Funktion nicht implementieren:

    // p zeigt auf ein Objekt das bereits von einem shared_ptr kontrolliert wird
    shared_ptr<Thing> GetSharedPtr(Thing* p)
    {
        return ...?
    }
    

    Mit einem klassischen intrusive_ptr ist das überhaupt kein Problem.



  • enable_shared_from_this...



  • hustbaer schrieb:

    314159265358979 schrieb:

    hustbaer schrieb:

    Was nicht mehr geht (oha, ein Nachteil von shared_ptr der ja angeblich keine Nachteile hat, na sowas), ist ... wenn du nen rohen Zeiger bekommst kannst du keinen shared_ptr mehr draus machen, d.h. du kannst kein "retain" machen.

    _Was genau_ geht hier angeblich mit shared_ptr nicht?

    Du kannst diese Funktion nicht implementieren:

    // p zeigt auf ein Objekt das bereits von einem shared_ptr kontrolliert wird
    shared_ptr<Thing> GetSharedPtr(Thing* p)
    {
        return ...?
    }
    

    Mit einem klassischen intrusive_ptr ist das überhaupt kein Problem.

    Wieso sollte man sowas machen? Aber trotzdem:

    void null_deleter(void*){}
    
    shared_ptr<Thing> GetSharedPtr(Thing* p)
    {
        return shared_ptr<Thing>(p, &null_deleter);
    }
    

    Ist auch nicht schwachsinniger als die Ausgangssituation...



  • Deine Lösung ist übrigens falsch Tachyon, da der Besitz auch wirklich geteilt werden muss. Denn sonst wird das Objekt gelöscht, wenn der letzte shared_ptr gelöscht wird, von dem der Zeiger kommt. 😉



  • Tachyon schrieb:

    void null_deleter(void*){}
    
    shared_ptr<Thing> GetSharedPtr(Thing* p)
    {
        return shared_ptr<Thing>(p, &null_deleter);
    }
    

    Ich denke, hustbaer ging es darum, dass beim Löschen des ursprünglichen shared_ptr dieser neue shared_ptr das Objekt am Leben halten soll.



  • 314159265358979 schrieb:

    Deine Lösung ist übrigens falsch Tachyon, da der Besitz auch wirklich geteilt werden muss. Denn sonst wird das Objekt gelöscht, wenn der letzte shared_ptr gelöscht wird, von dem der Zeiger kommt. 😉

    Ich habe den Thread gar nicht gelesen, um ehrlich zu sein. Aber das es ohnehin Quark ist, habe ich ja bereits angedeutet.



  • Natürlich, wenn shared_ptr, sollte sich das schon konsequent durch die ganze API ziehen, so daß alle entsprechenden Funktionen nur Objekte in shared_ptr annehmen (gibts eigentlich sinnvolle Möglichkeiten, zu erzwingen, daß ich z.B. von Node oder deren Unterklassen keine Instanzen ohne shared_ptr erstellen kann?)

    Wie es aussieht, müsste ich mich dann selbst um eine Smartpointer Implementierung kümmern, da ich im Android NDK keine Boost zur Verfügung habe.



  • Baldur schrieb:

    gibts eigentlich sinnvolle Möglichkeiten, zu erzwingen, daß ich z.B. von Node oder deren Unterklassen keine Instanzen ohne shared_ptr erstellen kann?

    Vielleicht was mit privaten ctor und ner Factory als friend? Aber ich fänd' es generell erstmal ziemlich gemein, wenn eine Klasse, die ich eigentlich gerne benutzen möchte, mich so sehr bevormunden würde. 😉

    Edit: Sorry, Schwachsinnsidee, da es ja auch um Ableitungen geht.



  • 314159265358979 schrieb:

    enable_shared_from_this...

    enable_shared_from_this ist wieder intrusive (man muss die Pointee-Klasse modifizieren).
    Implementiere es ohne "Thing" zu modifizieren.

    Baldur schrieb:

    gibts eigentlich sinnvolle Möglichkeiten, zu erzwingen, daß ich z.B. von Node oder deren Unterklassen keine Instanzen ohne shared_ptr erstellen kann?

    Jain.
    Wenn die abgeleiteten Klassen "mittun", dann geht das sehr einfach: verpass der Node-Klasse eine Factory-Funktion (idealerweise gleich ein Template), und mach den Ctor von Node und allen abgeleiteten Klassen protected bzw. private.

    Wenn die abgeleiteten Klassen nicht "mittun" hast du denke ich keine Chance.

    Folgendes findet sich z.B. in einem meiner Projekte

    template <class T>
    	inline shared_ptr<T> Control::CreateControl(shared_ptr<GuiSystem> const& guiSystem)
    	{
    		shared_ptr<T> control(new T(guiSystem), Deleter());
    		control->Control::PrivateDoPostConstruction();
    		return control;
    	}
    
    	template <class T, class A1>
    	inline shared_ptr<T> Control::CreateControl(shared_ptr<GuiSystem> const& guiSystem, A1 const& a1)
    	{
    		shared_ptr<T> control(new T(guiSystem, a1), Deleter());
    		control->Control::PrivateDoPostConstruction();
    		return control;
    	}
    
    	template <class T, class A1, class A2>
    	inline shared_ptr<T> Control::CreateControl(shared_ptr<GuiSystem> const& guiSystem, A1 const& a1, A2 const& a2)
    	{
    		shared_ptr<T> control(new T(guiSystem, a1, a2), Deleter());
    		control->Control::PrivateDoPostConstruction();
    		return control;
    	}
    
    	// ... versionen mit 3, 4, 5 parametern
    
    	inline void Control::PrivateDoPostConstruction()
    	{
    		m_lifecyclePhase = LifecyclePhase_PostConstruction;
    		PostConstructor();
    		m_lifecyclePhase = LifecyclePhase_FullyInitialized;
    		AssertPostConstructorCalled();
    	}
    

    (Ist für VS 2005, daher kein perfect forwarding, variadic template etc.)

    Abgeleitete (GUI-)Controls die nicht als Basisklasse taugen machen ihren Ctor private und erklären Control zum Freund. Und abgeleitete Controls die als Basisklasse taugen machen den Ctor protected.

    ps @all
    Wenn das so schlimmer Quark wäre gäbe es vermutlich enable_shared_from_this nicht. Das Argument dass man sowas nicht braucht wenn man ordentlich arbeitet lasse ich also in diesem Fall nicht gelten.



  • hustbaer schrieb:

    314159265358979 schrieb:

    enable_shared_from_this...

    enable_shared_from_this ist wieder intrusive (man muss die Pointee-Klasse modifizieren).
    Implementiere es ohne "Thing" zu modifizieren.

    Aber nur, wenn man diese Funktionalität braucht. Bei intrusivem Referece Counting muss man das mit jeder Klasse machen.
    Ansonsten kann man natürlich auch noch eine globale Liste an weak_ptr führen. Der Sinn davon sei mal dahingestellt.



  • 314159265358979 schrieb:

    Aber nur, wenn man diese Funktionalität braucht. Bei intrusivem Referece Counting muss man das mit jeder Klasse machen.

    Richtig.

    Ansonsten kann man natürlich auch noch eine globale Liste an weak_ptr führen. Der Sinn davon sei mal dahingestellt.

    Ja, irgendwie kann man das schon reinprügeln. Hätte aber mehr Nachteile als Vorteile.

    Weitere Vorteile von Intrusive Ref Counting:
    Man kann es unglaublich einfach implementieren (-> KISS).
    Man kann es (u.a. dadurch) schön in DLL Schnittstellen verwenden, in denen man (aus welchem Grund auch immer) keine Dependency auf diverse Libs wie Boost, CRT usw. haben darf.
    Man kann es dadurch auch schön Sprachübergreifend verwenden.

    Die einzige Möglichkeit die ich kenne, wie man sowas mit einer nicht-intrusive Lösung hinbekommt, ist globale Tables zu verwenden. Was viele viele Nachteile mitbringt, weswegen ich das für die meisten realen Projekte als "no go" klassifizieren würde.

    Viel davon geht auch mit shared_ptr und enable_shared_from_this , aber nur mit mMn. schwer zu vertretendem Aufwand. Vor allem wenn man keinen echten Vorteil davon hat. Und den hat man in vielen Projekten nicht.

    Wenn man Features wie weak_ptr braucht, die shared_ptr bereits mitbringt, man sich mit einer (selbstgebackenen) intrusive Lösung aber umständlich dazustricken müsste, sieht die Sache wieder anders aus.

    Jetzt Frage: bist du immer noch der Meinung dass Intrusive Ref Counting keinerlei Vorteile hat?
    Das war nämlich deine Behauptung die ich kritisiert habe.

    Etwas diplomatischer formuliert wie z.B. "Intrusive Ref Counting hat wenig relevante Vorteile, und i.A. fährt man mit shared_ptr besser" wäre ja durchaus OK, da hätte ich sicher nix dagegen gesagt.

    Weil du Pi bist musst du aber immer mit absoluten Aussagen und Superlativen um dich werfen. Und dann passiert das, was hier wiedermal passiert ist. Weil ich das dann so nicht stehen lassen kann.



  • Wo das Thema ja hier sowieso schon etwas abgedriftet ist, aber die Sache mit Ownership angesprochen wurde, wollte ich mal fragen, wie ihr das so handhabt. Es wurde ja in den Raum gestellt, dass ein Objekt seine eigene Lebenszeit niemals verwaltet. Im Prinzip würde ich dem ja auch direkt zustimmen, weil es anderenfalls zu Fehlern kommt, wenn man es "falsch" verwendet.
    Sagen wir mal, wir haben so ein UI-Programm und auf einen Mausclick wird ein Widget erzeugt, das die Maus captured und sich mit dem nächsten Click und einer änderung am Datenmodell dahinter wieder verabschieden soll. Der erste Click geht ja zweifellos an ein anderes Ziel als dieses Widget, dieses erzeugt dann das Widget (bitte keine 2-Phasen-Erzeugung). Nun soll aber das erzeugte Widget die Abhandlung der nächsten Benutzereingaben durchführen. Da kommt man doch zu dem Problem, dass das Widget auf eine Benutzereingabe-Nachricht entscheiden soll, dass es fertig ist. Wer ist nun dafür zuständig, es zu entfernen? Soll es sich beim Erzeuger melden, dass es fertig ist? (Creator()->DeleteMe())? Das entspricht ja nun auch nur einem verkappten delete this.
    Gleiches Beispiel hätte man ja nun auch bei jedem beliebigen Dialog, der einen Schließen-Button aufweist.

    Wäre da mal an der sauberen (tm) Lösung interessiert!

    Viele Grüße,
    Michael


Anmelden zum Antworten