Smart Pointer



  • Erstmal muss begründet werden, weshalb überhaupt shared_ptr.

    struct Node
    {
        Node* parent; // nicht besitzender Zeiger
        std::vector<Node> children;
        std::vector<Attribute> attributes;
        std::string data;
    };
    


  • Es gibt noch Spezialisierungen von XmlNode.



  • Warum nicht std::unique_ptr ? Der ist generell die erste Wahl bei Smart-Pointern. std::shared_ptr ist für Spezialfälle.



  • Nexus schrieb:

    Warum nicht std::unique_ptr ? Der ist generell die erste Wahl bei Smart-Pointern. std::shared_ptr ist für Spezialfälle.

    Vielleicht weil er std::weak_ptr benötigt und die nicht in Kombination mit std::unique_ptr funktionieren?



  • TNA schrieb:

    Vielleicht weil er std::weak_ptr benötigt und die nicht in Kombination mit std::unique_ptr funktionieren?

    weak_ptr benötigt man nie einfach so, sondern immer nur in Verbindung mit shared_ptr .

    Aber dann frag ich eben anders: Warum reicht unique_ptr hier nicht aus?



  • Hmm, das stimmt natürlich. Wenn sichergestellt ist, das ein Child den Parent nie überlebt und das so gewünscht ist, sollte das passen. Wenn das multithreaded ist dürfte das allerdings eventuell sehr kritischen, schwierig zu behebenden Race Conditions führen.



  • Enumerator schrieb:

    jeder Parent-Node auf seinen Child-Node verweist und umgekehrt

    Hab ich jetzt was nicht ganz verstanden oder wie soll man das mit einem unique_ptr hinbekommen?



  • Ein normaler Zeiger als Child->Parent.



  • anti-freak schrieb:

    Enumerator schrieb:

    jeder Parent-Node auf seinen Child-Node verweist und umgekehrt

    Hab ich jetzt was nicht ganz verstanden oder wie soll man das mit einem unique_ptr hinbekommen?

    "Umgekehrt" ist weiterhin ein normaler Pointer. Warum sollte es auch ein smart pointer sein?



  • Du solltest dir klar sein, was die pointer bedeuten.
    unique_ptr bedeutet, das Objekt hat einen klaren Besitzer
    shared_ptr bedeutet, das Objekt hat mehrere Besitzer
    weak_ptr kann auf ein geteiltes Objekt zugreifen, aber besitzt es nicht.
    Ein normaler pointer zeigt nur und hat keine weiteren Bedingungen

    Wie Kellerautomat und manni64 geschrieben haben, wenn du davon ausgehst, dass ein Child NUR EINEN Parent haben kann und bei dessen Löschen auch gelöscht wird, dann besitzt der Parent das Child, also unique_ptr.
    Das Child greift nur auf sein Parent zu, aber weder löscht, noch räumt es seinen Parent auf (wie auch, schließlich sollte es andersherum sein), dann gehört hier ein normaler pointer hin.

    Würde man für beides shared_ptr verwenden, hätte man hier circular references, wodurch die beide Objekte ewig bestehen bleiben würden: memory leaks oder andere blöde Nebeneffekte.



  • TNA schrieb:

    Wenn sichergestellt ist, das ein Child den Parent nie überlebt und das so gewünscht ist, sollte das passen. Wenn das multithreaded ist dürfte das allerdings eventuell sehr kritischen, schwierig zu behebenden Race Conditions führen.

    Die Aussage ist so für sich genommen mMn. Quatsch.

    Wenn du anderer Meinung bist, dann beschreibe bitte wenigstens einen Fall der hier Probleme verursachen könnte. (Inklusive Begründung warum der selbe Fall ohne Multithreading zu keinem Problem führt, sofern dies nicht offensichtlich sein sollte.)



  • Wenn ein Thread gerade eine Operation auf einer Node ausführt bei der der Parent Zeiger involviert ist während ein anderer Thread gerade den Parent entfernt.



  • TNA schrieb:

    Wenn ein Thread gerade eine Operation auf einer Node ausführt bei der der Parent Zeiger involviert ist während ein anderer Thread gerade den Parent entfernt.

    Da werden dir smart pointer als Zeiger auf den Parent nicht weiterhelfen, denn den musst du ja auch irgendwann umsetzen bzw. löschen. Du wirst nicht drumrum kommen die Zugriffe zu synchronisieren.



  • manni66 schrieb:

    TNA schrieb:

    Wenn ein Thread gerade eine Operation auf einer Node ausführt bei der der Parent Zeiger involviert ist während ein anderer Thread gerade den Parent entfernt.

    Da werden dir smart pointer als Zeiger auf den Parent nicht weiterhelfen, denn den musst du ja auch irgendwann umsetzen bzw. löschen. Du wirst nicht drumrum kommen die Zugriffe zu synchronisieren.

    Warum? Wenn ich einen Weak Pointer auf den Parent habe hole ich mir daraus einen shared Pointer. Entweder das schlägt fehl, dann weiß ich, das der Parent weg ist oder eben nicht, dann kann ich sicher sein, dass der Parent noch mindestens so lange lebt wie ich den Ponter in der Hand habe. Für das Problem "Mir zieht jemand die Node unterm Hintern weg" braucht man dann keine Synchronisierung. Synchronisierung braucht man zwar eventuell an anderer Stelle trotzdem, kann dann aber sehr viel fein granularer Synchronisieren.

    Hab gerade deinen Betrag noch mal genau gelesen. Wenn man bestehende Nodes umsetzen will und nicht nur löschen/einfügen geht das wohl tatsächlich nicht mehr ohne explizites synchronisieren.



  • Die Anwendung ist noch single threaded wird aber später multithreaded laufen. Es kommt noch schlimmer, es ist ein GUI Framework mit Styles die wiederum auf andere Resourcen verlinken können. Resourcen aber auch wieder auf andere Resourcen. Auch die Controls sind natürlich miteinander verlinkt. D.h. in der Regel ist der Owner von z.B. einer Resource nicht eindeutig und Unique-Pointer kommen schonmal gar nicht in Frage. Also auf gut deutsch diese ganzen SmartPointer sind in Wirklichkeit ziemlich dumm und lesen sich nur auf dem Papier gut. In der Praxis sind sie eine jämmerliche Krücke um die ich wild herumfrickeln müsste. Einheitlich kann ich das ganze auch nicht lösen. Damit sind "SmartPointer" vom Tisch. Dazu kommt der wahrscheinlich hohe Performanceverlust. Bleibt also nur wie in dem anderen Thread von mir genannte Garbage Collection. Was vielleicht noch ginge: Alle Klassen sind von einer gemeinsamen Basis-Klasse abgeleitet. Kann ich nicht in dieser Basis-Klasse einen Mechanismus implementieren der das löschen regelt?



  • Enumerator schrieb:

    Einheitlich kann ich das ganze auch nicht lösen. Damit sind "SmartPointer" vom Tisch. Dazu kommt der wahrscheinlich hohe Performanceverlust.

    SmartPointer sind in dem was sie tun extrem performant. Glaubst du wirklich, du bekommst diese Funktionalität, wenn du sie wirklich brauchst, irgendwie billiger, zumal du offenbar noch viel komplexere Sachen machen möchtest als mit den SmartPointern möglich? Mit einem GC sicherlich nicht.



  • Aus einem WeakPtr zunächst einen SharedPtr zu erzeugen um festzustellen um ein Objekt existiert ist in meinen Augen nicht gerade performant. Das würde mir den Renderingprocess vermutlich extrem ausbremsen. Der GC könnte dagegen wenn das Programm gerade Idle ist was bei einer GUI oft der Fall ist aufräumen.



  • Enumerator schrieb:

    Es kommt noch schlimmer, es ist ein GUI Framework mit Styles die wiederum auf andere Resourcen verlinken können. Resourcen aber auch wieder auf andere Resourcen. Auch die Controls sind natürlich miteinander verlinkt.

    Ist das Framework für C++ oder für eine andere Sprache?

    Bei Controls sehe ich nicht ein, wieso der Parent nicht das Child besitzen kann. Qt macht das zum Beispiel so. unique_ptr heisst ja nicht, dass der Besitz nicht wechseln kann, Besitz ist nur ein temporäres Verhältnis.

    Styles die auf Resourcen verlinken sind auch in Ordnung und Resourcen, die untereinander verlinkt auch, aber nur, wenn keine zirkuläre Verlinkung da ist. Was soll ich mir unter zwei Ressourcen, die sich gegenseitig verlinken vorstellen? Da reichen shared_ptrs und Performance ist auch nicht so wichtig, da das eher statisch sein sollte. Ansonsten machst du dir halt einen eigenen Memory-Pool, der refcounted ist.



  • Enumerator schrieb:

    Aus einem WeakPtr zunächst einen SharedPtr zu erzeugen um festzustellen um ein Objekt existiert ist in meinen Augen nicht gerade performant. Das würde mir den Renderingprocess vermutlich extrem ausbremsen. Der GC könnte dagegen wenn das Programm gerade Idle ist was bei einer GUI oft der Fall ist aufräumen.

    Ist dieser Baum überhaupt der Flaschenhals bzw. ist das ganze so performancekritisch? Braucht man überhaupt ständig den Parent Zeiger? Darüber würde ich mir erst mal im klaren sein wollen, bevor ich viel Zeit in die Lösung von Problemen stecke die vielleicht gar nicht existieren. Selbst wenn die Antwort Ja sein sollte, hilft hier auch nur messen.



  • Was macht weak_ptr::lock denn genau?
    Der guckt doch nur ob sein zugehöriger elterlicher shared_ptr (noch) da ist, kopiert diesen dann und gibt die Kopie an den Aufrufer zurück.

    Wenn das wirklich der Part sein sollte, der deiner Performance das Genick brechen sollte, dann hast du an anderen Stellen eher geschlampt.


Anmelden zum Antworten