Instanzenlogger - optimal?



  • Ich will eine new/delete-Überladung für die Lösung des DLL-Heap-Problems mit
    einem Logger verbinden.

    Und zwar:

    Ein Logger soll bei jedem new und new[] den Pointer des erzeugten Speicherblocks
    in ein std::set tun, bei jedem delete / delete[] wird dann im set gesucht,
    ob der angegebene Pointer drinnen ist. Wenn ja -> löschen.

    Das ermöglicht ein Abfangen von Löschversuchen bei Objekten am Stack. Das ist
    manchmal sinnvoll, wenn man zB automatisch Ownership übertragen will. Beispiel:

    std::vector < Node* > nodes;
    
    ......
    
    Node n1;
    Node *n2 = getNodeFromSomewhere();
    
    nodes.push_back(&n1);
    nodes.push_back(&n2);
    
    ......
    
    for all n in nodes // (nein, ist kein C++ code, ist aber übersichtlicher als die STL-Wurst)
      delete n; // <- Das würde ohne das Abfangen crashen wg. n1
    
    // Und ja, ptr_vector löscht automatisch die Pointer, aber ich wollt das
    // Problem verdeutlichen
    

    Darüberhinaus soll eine Funktion, die man am Ende des Programms aufruft,
    nachschauen, ob am Ende das set leer ist. Wenn nicht -> Speicherblöcke
    freigeben, eine Liste zu cerr mit den nicht deallokierten Speicherblöcken
    schicken.

    Das hab ich alles bereits gebaut, nur: die set-Operationen sind nicht O(1).
    Wenn ich nun viele Objekte schnell anlege/deallokiere, fürchte ich, dass das
    zu einer Bremse werden würde. Und da es hier um ein Grundelement geht, mach ich
    mir jetzt schon Gedanken darüber (nachträgliche Änderungen könnten fatal sein).

    Die Frage ist nun, ob es wirklich den Aufwand wert ist, Ausschlussmöglichkeiten
    zu bieten (also dass ein Objekt nicht ins set getan wird), und wenn ja, wie am besten.

    Und, ein set ist doch der beste Container dafür, oder?



  • Der beste Container wäre IMHO ein hash_set oder eine hash_map.
    Aber.
    Ich halte die Idee "das Heap Problem" mittels überladung von new/delete zu lösen für unschlau. Entweder du machst eine schöne C++ DLL, dann kannst du ohne schlechtes Gewissen verlangen dass wer auch immer die DLL verwendet den selben Heap verwendet, da du sowieso davon ausgehen musst dass der selbe Compiler verwendet wird (C++ definiert keine ausreichenden Regeln dafür wie ein bestimmtes Objekt im Speicher auszusehen muss - ergo wird das ein Compiler anders machen als ein anderer - ergo kann man auch nicht vernünftig C++ Code verschiedener Compiler mischen). Oder aber du machst ein schönes C Interface, dann kannst du eine DLL machen die man verwenden kann egal mit was für einem Compiler man arbeitet. Dann kannst du einen "header-only" C++ Wrapper dafür schreiben damit man es in C++ schön verwenden kann. Das Heap Problem verschwindet damit auch automatisch da du dann irgendwelche "NewFoo" und "DeleteFoo" Funktionen in deinem C-Interface haben wirst.

    Das "n1 Problem" lösen zu wollen halte ich auch für eine etwas ... wie soll ich sagen ... "unglückliche Idee". Wenn du einen Fall hast wo du deine DLL bzw. irgendein System unbedingt vor so schwerwiegenden Fehlern von aussen schützen möchtest dann verwende gleich Handles. Also z.B. irgendwelche Zahlen die dein Programm nach bestimmten Regeln zusammenbaut die du dann schnell auf Gültigkeit prüfen kannst und am besten noch einfach in einen Offset in ein Array umrechnen kannst wo du die Zeiger auf die eigentlichen Objekte ablegst. Dabei solltest du aufpassen bestimmte Dinge zu vermeiden, wie z.B. besonders kleine Zahlen als Handles (finden sich oft in nicht initialisiertem Speicher), ein Handle welches ich jetzt anlege sollte nicht der Wert der letzten welches ich angelegt habe + 1 sein, und die Zahlen sollten nicht allzuschnell "wiederverwertet" werden nachdem ein Handle freigegeben wurde.

    Wenn du das nur machen möchtest um damit explizit zu ermöglichen dass jmd. delete auf etwas aufruft was nicht deleted werden sollte dann ist das denke ich der komplett falsche Ansatz, das führt bloss zu grausam unsauberen Programmen.

    Was vielleicht Sinn machen kann ist IN DER DEBUG VERSION an den Anfang aller Objekte eine "Magic Number" zu setzen die du dann mittels eines ASSERT auf Gültigkeit prüfen kannst, alles andere halte ich für Zwecklos und gefählich.

    Ahja, wenn du was "ownership" angeht etwas flexibler sein willst verwende entweder smart pointer ala boost::shared_ptr oder verwende einen einfachen Wrapper um den Pointer der weiss ob er owner ist oder nicht (bool flag). Oder natürlich reference counting.


Anmelden zum Antworten