Entfernen von STL std :: queue ohne das entfernte Objekt zu zerstören?

7

Die gesamte Dokumentation, die ich zu den STL-Containern (Warteschlange und Liste) finden kann, besagt, dass für jede der remove-Funktionen der Destruktor des entfernten Objekts aufgerufen wird. Das bedeutet, dass ich std :: queue nicht immer verwenden kann, wenn ich eine Warteschlange möchte, die einfach eine Liste von Objekten darstellt, für die eine Operation ausgeführt werden muss.

Ich möchte in der Lage sein, Objekte zur Warteschlange hinzuzufügen, wenn sie darauf warten, dass ich ihnen etwas antworte. Dann möchte ich sie entfernen, wenn ich damit fertig bin, ohne das fragliche Objekt zu zerstören. Dies scheint aus der Dokumentation, die ich gelesen habe, nicht möglich zu sein. Lese ich die Dokumentation falsch? Gibt es einen anderen Typ von Warteschlange in der STL als die grundlegende "Warteschlange", die den Destruktor des entfernten Objekts bei einem Aufruf von pop_front nicht aufruft?

Edit zur Klärung: In meinem Fall verwende ich eine Liste von Zeigern. Etwas wie das:

%Vor%     
Daniel Bingham 03.10.2009, 17:16
quelle

5 Antworten

17

Wenn Sie Zeiger auf Objekte in der Warteschlange (und jedem anderen STL-Container) setzen, werden die Zeiger nicht gelöscht, wenn Sie sie entfernen.

Um es näher zu sagen: Wenn Sie std :: queue benutzen und ein Objekt entfernen, wird der Destruktor von some_obj * aufgerufen. Aber der Destruktor für einen einfachen Zeiger (oder einen beliebigen POD-Typ - int, char usw.) ist leer, no-op. Die feine Linie hier ist, dass der Destruktor für some_obj * sich sehr vom Destruktor für some_obj unterscheidet.

    
sbk 03.10.2009, 17:28
quelle
7

STL-Container haben Wert Semantik. Wenn Sie ein Objekt in einen STL-Container verschieben, behält der STL-Container seine eigene Kopie des Objekts, und wenn das Objekt (interne Kopie) aus dem Container entfernt wird, wird es zerstört.

Wenn Sie einen Container eines Proxytyps als rohe Zeiger, intelligente Zeiger (shared_ptr, weak_ptr) oder Adapter (wie boost :: reference_wrapper) verwendet haben, zerstört der STL-Container den Proxy, aber nicht den Typ. Die Entscheidung über die anderen hängt normalerweise davon ab, wie Sie mit Ressourcen umgehen wollen.

Das gebräuchlichste Idiom verwendet rohe Zeiger, aber sie geben nicht explizit an, wer für die Zerstörung verantwortlich ist (der Code, der den Container abruft, sollte den Zeiger löschen, oder die Ressource wird woanders behandelt?).

Die moderne Nutzung bewegt sich in Richtung des shared_ptr-Ansatzes, da sie das Besitzproblem verwässert. Das Objekt wird garantiert lebendig, wenn Sie es aus dem Container entfernen, und wenn niemand sonst ein shared_ptr hält, wird das Objekt automatisch gelöscht, wenn der lokale shared_ptr den Gültigkeitsbereich verlässt. Durch die Verwendung von weak_ptr behalten Sie den Besitz im ursprünglichen Code bei, können aber vor der Verwendung überprüfen, ob der Zeiger gültig ist (falls er gelöscht wurde). Dadurch können Sie vermeiden, dass die Operation für ein Objekt ausgeführt wird, das sofort entfernt wird.

Das Problem mit dem Ansatz shared_ptr / weak_ptr besteht darin, dass Sie gezwungen sind, shared_ptr für die ursprüngliche Ressource zu verwenden. Dies bedeutet, dass Sie einen Zeiger nicht in ein Unterobjekt (Mitgliedsattribut) einer anderen Klasse einfügen können, ohne die Klasse neu zu entwerfen, um das Attribut über shared_ptr zu halten, und dies hat andere Auswirkungen (die Attribute sind nicht mehr zusammenhängend im Speicher) , dynamischere Zuweisungsoperationen werden benötigt ...)

Eine Technik, die kaum gesehen wird, ist die Verwendung von Adaptern als boost :: reference_wrapper & lt; & gt; Ein Referenzwrapper ist ein Proxy-Objekt, das einen Verweis auf das ursprüngliche Objekt enthält und selbst kopierbar ist. Der Vorteil gegenüber einfachen unformatierten Zeigern besteht darin, dass beim Lesen des Codes klar ist, dass die Ressource außerhalb der Warteschlange verwaltet wird: Der Code, der Daten aus der Warteschlange abruft, muss das Objekt nicht löschen. Der Vorteil gegenüber dem Smart-Pointer-Ansatz besteht darin, dass Sie andere Teile Ihres Systems nicht neu entwerfen müssen, um Smartpointer zu verwenden. Der Nachteil ist, dass Sie, wie beim Ansatz mit dem rohen Zeiger, sicherstellen müssen, dass die Lebensdauer des referenzierten Objekts die Referenz im Container überlebt.

    
quelle
4
%Vor%

Wenn Sie someobj_t nicht kopieren möchten, können Sie std::queue< shared_ptr<someobj_t> > verwenden.

    
Kirill V. Lyadvinsky 03.10.2009 17:21
quelle
3

Wie wäre es mit einer Liste von Zeigern zu den Objekten?

    
John Carter 03.10.2009 17:20
quelle
2

Denken Sie an Objekte in einem Container, der sich in diesem Container befindet. Wenn ein Objekt aus einem Container entfernt wird, ist es genauso, als würde man den Bereich einer Funktion verlassen. Wenn die Variable ein Zeiger ist, passiert nichts beim Verlassen des Bereichs. Wenn Variable ein stack local ist, wird Destruktor wird automatisch beim Verlassen des Bereichs aufgerufen.

Das Speichern von Zeigern in Containern hat dieselben Nachteile wie die Zuweisung in einen lokalen Raw-Pointer. Der Speicher wird nicht automatisch bereinigt. In einer Funktion, wenn Sie den Zeiger nicht löschen oder die Eigentumsrechte übertragen, indem Sie ihn zurückgeben, haben Sie einen Speicherverlust.

Wenn rohe Zeiger in einem Container gespeichert werden, kann der Besitz ein wenig mehrdeutig werden und es kann leicht zu Undichtigkeiten kommen. Sehen Sie sich tr1 :: shared_ptr an und speichern Sie diese stattdessen in den Containern.

std :: unique_ptr in C ++ 0x wäre auch eine gute Lösung, um einen Zeiger in einem stdlib-Container zu speichern, wenn er verfügbar ist.

    
joshperry 03.10.2009 17:42
quelle

Tags und Links