Frage bezüglich der Deallokierung/Ownership von Elementen



  • Der Use-Case: es gibt eine Szene, und Objekte darin. Beide sind modelliert als Klassen "scene" und "item". Es soll möglich sein, einer Szene Objekte hinzuzufügen und wegzunehmen. Darüberhinaus sollte scene die Verwaltung der Deallokierung der items übernehmen, d.h. scene soll "Ownership" der items haben.

    Es sind zwei Wege denkbar: entweder, der Programmierer erzeugt das item selbst (auf dem Stack oder auf dem Heap), und fügt es der scene hinzu mit einer add_item() Funktion. Oder, es gibt eine create_item() Funktion in scene, die das Erzeugen übernimmt. Da item eine abstrakte Basisklasse ist, müsste create_item() ein Template sein, aber das ist kein Problem.

    Die create_item() Methode gefällt mir, weil man intern zB shared_ptr verwenden kann, sicher sein kann, dass ein Item nicht auf dem Stack ist usw. allerdings ist es dann nicht möglich, ein item nur für die dauer eines Scopes zu beinhalten. Z.B.

    {
      my_item item;
      scene.add_item(item);
    
      ..
    
      // item wird hier entfernt, und der item-Destruktor entfernt das item automatisch von der scene
    }
    

    wäre nicht so trivial und elegant möglich. Man könnte eine Art scoped_item einführen, aber das wäre halt ein Extra. (Es stellt sich natürlich die Frage, wie sinnvoll es wäre, so ein scoped item überhaupt zu haben.)

    Die andere Variante wäre eben manuelles Erzeugen und add() danach. Sie ist u.U. eleganter und "sauberer", und erlaubt Code wie der obige, aber hat ein Problem mit der Unterscheidung Stack vs. Heap. Wenn ich ein am Stack angelegtes item übergebe, und scene packt das in ein shared_ptr, wirds nicht gut ausgehen.

    Eine Lösung, die ich mir denken kann: in der item-Basisklasse die new/delete operatoren überladen, und in denen eine Logik einbauen, die bei new() den Pointer des erzeugten items in ein STL set packt. Bei delete() wird dann nachgesehen, ob der Pointer in dem set ist; wenn nicht, dann geschieht nichts in delete, wenn ja, wird das "eigentliche" delete aufgerufen. Das hat die Nachteile, dass das Set Extra-Overhead darstellt, und dass das Ganze u.U. nicht so stabil und sicher ist, zB wenn jemand explizit mit ::new und ::delete arbeitet.

    Was meint ihr?



  • warum alles so doll kompliziert?
    würde es nicht reichen, dem konstruktor von ItemBase eine referenz auf die scene zu übergeben und die ownership der klasse world zu übergeben?
    dann kannste langlebige sachen mit new anlegen und der world schenken, kurzlebige auch im local scope lassen und die scene hat dennoch immer eine aktuelle liste aller items.



  • Mit anderen Worten, die Ownership-Frage auslagern in eine eigene Struktur (in dem Fall "world"). Darüber habe ich schon nachgedacht. Was mir daran gefällt ist die Orthogonalität; Scene, Item etc. müssen nix mehr über Ownership wissen. Es besteht aber die Möglichkeit eines Memoryleaks, wenn man vergisst, nach dem Erzeugen des Items dieses der World-Klasse hinzuzufügen. Das klingt für mich wie ein Fehler, der häufig vorkommen könnte. Darüberhinaus musst du überall die World-Instanz durchreichen, und es wirkt etwas seltsam, dass du manche Sachen zusätzlich auch der World-Instanz hinzufügst und manche nicht.

    EDIT:

    Eine weitere Möglichkeit, die mir einfällt, sind Object-Trees: Children werden von den Parents deallokiert. Wenn man was am Stack haben will, übergibt man dem Item als Parent einfach ein Nullpointer. Kein Parent -> keine Deallokierung. Qt funktioniert so.



  • Ich bevorzuge ebenfalls die QT-Herangehensweise. Per Default ist der Parent fuer das Aufraeumen zustaendig. Will man eine andere Policy, muss man dieses explizit anfordern.

    Gruss Kimmi



  • Ich würde hier ganz normal shared_ptr verwenden, und der add_item Funktion direkt den shared_ptr mitgeben.
    Der Aufrufer *kann* dann natürlich Unsinn treiben, indem er etwas in einen shared_ptr steckt, was nix in einem shared_ptr verloren hat (z.B. ein Item welches auf dem Stack alloziert wurde). Bloss Unsinn treiben kann man immer irgendwie.

    Und bevor du dir Gedanken machst wie man es ermöglichen könnte "Stack-Items" zu ermöglichen, solltest du dich erstmal fragen in wievielen Fällen das überhaupt jemals vorkommen könnte. Und ob es sich lohnt für den (wahrscheinlich minimalen bis nicht-vorhandenen) Geschwindigkeits-Vorteil, etwas komplizierter zu lösen, als es notwendig wäre.

    Eine andere Möglichkeit wäre Intrusive-Reference-Counting zu verwenden (intrusive_ptr). Das macht Qt soweit ich weiss. Das hätte den Vorteil dass du u.U. auch interlocked Funktionen verzichten kannst, nämlich wenn du deine Items immer nur aus einem Thread angreifst, bzw. dich ggf. selbst um die nötige Synchronisation kümmerst. Bringt aber auch nur einen nennenswerten Vorteil wenn oft Referenzen auf Objekte "dazugemacht" bzw. entfernt werden (=wenn oft Smart-Pointer kopiert werden müssen).


Anmelden zum Antworten