Ich verwende std :: queue zum Implementieren der JobQueue-Klasse. (Grundsätzlich verarbeitet diese Klasse jeden Job auf FIFO-Weise). In einem Szenario möchte ich die Warteschlange auf einmal löschen (alle Jobs aus der Warteschlange löschen). In der Klasse std :: queue ist keine eindeutige Methode verfügbar.
Wie implementiere ich die Clear-Methode für die JobQueue-Klasse effizient?
Ich habe eine einfache Lösung, um in eine Schleife zu springen, aber ich suche nach besseren Wegen.
//Clears the job queue
void JobQueue ::clearJobs()
{
// I want to avoid pop in a loop
while (!m_Queue.empty())
{
m_Queue.pop();
}
}
deque
unterstützt klarAntworten:
Eine gängige Redewendung zum Löschen von Standardcontainern ist das Austauschen mit einer leeren Version des Containers:
Dies ist auch die einzige Möglichkeit, den in einigen Containern gespeicherten Speicher tatsächlich zu löschen (std :: vector).
quelle
std::queue<int>().swap(q)
. Mit Copy and Swap Idiom sollte dies alles gleichbedeutend sein mitq = std::queue<int>()
.std::queue<int>().swap(q)
entspricht zwar dem obigen Code,q = std::queue<int>()
muss jedoch nicht gleichwertig sein. Da bei der Zuweisung des zugewiesenen Speichers keine Eigentumsübertragung stattfindet, rufen einige Container (wie der Vektor) möglicherweise nur die Destruktoren der zuvor gehaltenen Elemente auf und legen die Größe (oder eine äquivalente Operation mit den gespeicherten Zeigern) fest, ohne den Speicher tatsächlich freizugeben.queue
hat keineswap(other)
Methode,queue<int>().swap(q)
kompiliert also nicht. Ich denke, Sie müssen das Generikum verwendenswap(a, b)
.Ja - ein bisschen eine Fehlfunktion der Warteschlangenklasse, IMHO. Das ist was ich mache:
quelle
swap
"effektiver" istq1.swap(queue<int>());
q1=queue<int>();
ist sowohl kürzer als auch klarer (Sie versuchen es nicht wirklich.swap
, Sie versuchen es.clear
).q1 = {}
genugqueue<T>
entspricht der leeren Argumentliste{}
und ist implizit, daher wird er aufgerufen undq1.operator=(queue<T>&&)
verbraucht dann die neu erstelltequeue
Der Autor des Themas fragte, wie die Warteschlange "effizient" gelöscht werden kann, daher gehe ich davon aus, dass er eine bessere Komplexität als lineares O (Warteschlangengröße) wünscht . Verfahren nach diente David Rodriguez , Anon die gleiche Komplexität: gemäß STL Referenz,
operator =
hat die Komplexität O (Warteschlangengrße) . IMHO liegt es daran, dass jedes Element der Warteschlange separat reserviert ist und nicht wie im Vektor in einem großen Speicherblock zugeordnet ist. Um den gesamten Speicher zu löschen, müssen wir jedes Element separat löschen. Der einfachste Weg zum Löschenstd::queue
ist also eine Zeile:quelle
O(n^2)
Algorithmus über einenO(n)
Algorithmus stellen, wenn die Konstanten der linearen Operation ihn für alle langsamer als das Quadrat machen, esn < 2^64
sei denn, ich hätte einen starken Grund zu der Annahme, dass ich den IPv6-Adressraum oder ein anderes spezifisches Problem durchsuchen muss. Leistung in der Realität ist mir wichtiger als Leistung am Limit.Anscheinend gibt es zwei naheliegendste Möglichkeiten zum Löschen
std::queue
: Tauschen mit leerem Objekt und Zuweisen zu leerem Objekt.Ich würde vorschlagen, die Zuweisung zu verwenden, da sie einfach schneller, lesbarer und eindeutiger ist.
Ich habe die Leistung anhand des folgenden einfachen Codes gemessen und festgestellt, dass das Auslagern in der C ++ 03-Version 70-80% langsamer funktioniert als das Zuweisen zu einem leeren Objekt. In C ++ 11 gibt es jedoch keinen Leistungsunterschied. Wie auch immer, ich würde mit Auftrag gehen.
quelle
In C ++ 11 können Sie die Warteschlange folgendermaßen löschen:
quelle
Sie können eine Klasse erstellen, die von der Warteschlange erbt, und den zugrunde liegenden Container direkt löschen. Das ist sehr effizient.
Möglicherweise ermöglicht Ihre Implementierung auch, dass Ihr Warteschlangenobjekt (hier
JobQueue
) erbt,std::queue<Job>
anstatt die Warteschlange als Mitgliedsvariable zu haben. Auf diese Weise hätten Sie direkten Zugriff aufc.clear()
Ihre Mitgliedsfunktionen.quelle
Angenommen, Ihre
m_Queue
enthält ganze Zahlen:Andernfalls, wenn es z. B. Zeiger auf
Job
Objekte enthält, dann:Auf diese Weise tauschen Sie eine leere Warteschlange mit Ihrer aus
m_Queue
und werden somitm_Queue
leer.quelle
Ich möchte mich lieber nicht auf
swap()
ein neu erstelltes Warteschlangenobjekt verlassen oder es auf die Warteschlange setzen , da die Warteschlangenelemente nicht ordnungsgemäß zerstört werden. Der Aufrufpop()
ruft den Destruktor für das jeweilige Elementobjekt auf. Dies ist möglicherweise kein Problem in<int>
Warteschlangen, hat jedoch möglicherweise Nebenwirkungen auf Warteschlangen, die Objekte enthalten.Daher
while(!queue.empty()) queue.pop();
scheint eine Schleife mit leider die effizienteste Lösung zu sein, zumindest für Warteschlangen, die Objekte enthalten, wenn Sie mögliche Nebenwirkungen vermeiden möchten.quelle
swap()
oder Zuweisung ruft den Destruktor in der jetzt nicht mehr existierenden Warteschlange auf, der die Destruktoren aller Objekte in der Warteschlange aufruft. Wenn Ihre Warteschlange Objekte enthält, die tatsächlich Zeiger sind, ist dies ein anderes Problem - aber ein einfachespop()
hilft Ihnen auch dort nicht weiter.Ich mache das (mit C ++ 14):
Diese Methode ist nützlich, wenn Sie einen nicht trivialen Warteschlangentyp haben, für den Sie keinen Alias / Typedef erstellen möchten. Ich hinterlasse jedoch immer einen Kommentar zu dieser Verwendung, um ahnungslosen / Wartungsprogrammierern zu erklären, dass dies nicht verrückt ist und anstelle einer tatsächlichen
clear()
Methode erfolgt.quelle
myqueue = { };
das gut funktionieren wird.Die Verwendung von a ist
unique_ptr
möglicherweise in Ordnung.Anschließend setzen Sie es zurück, um eine leere Warteschlange zu erhalten und den Speicher der ersten Warteschlange freizugeben. Was die Komplexität betrifft? Ich bin mir nicht sicher - aber ich denke, es ist O (1).
Möglicher Code:
quelle
Eine andere Möglichkeit besteht darin, einen einfachen Hack zu verwenden, um den zugrunde liegenden Container abzurufen
std::queue::c
und ihn aufzurufenclear
. Dieses Mitglied mussstd::queue
gemäß Standard anwesend sein , ist es aber leiderprotected
. Der Hack hier wurde aus dieser Antwort genommen .quelle