it-swarm.com.de

Wie lösche ich die std :: queue effizient?

Ich verwende std :: queue zum Implementieren der JobQueue-Klasse. (Grundsätzlich verarbeitet diese Klasse jeden Job in der Art FIFO.) ..__: In einem Szenario möchte ich die Warteschlange auf einmal löschen (alle Jobs aus der Warteschlange löschen) . Ich sehe keine clear-Methode in der Klasse std :: queue verfügbar.

Wie implementiere ich die klare Methode für die JobQueue-Klasse effizient?

Ich habe eine einfache Lösung für das Poppen in einer Schleife, 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();
    }
}
140
aJ.

Ein allgemeines Idiom zum Löschen von Standardcontainern ist das Austauschen mit einer leeren Version des Containers:

void clear( std::queue<int> &q )
{
   std::queue<int> empty;
   std::swap( q, empty );
}

Dies ist auch die einzige Möglichkeit, den in einigen Containern gespeicherten Speicher tatsächlich zu löschen (std :: vector).

Ja - ein Missverständnis der Warteschlangenklasse, IMHO. Das ist was ich mache:

#include <queue>
using namespace std;;

int main() {
    queue <int> q1;
    // stuff
    q1 = queue<int>();  
}
40
anon

'David Rodriguez', 'anon' Der Autor des Themas fragte, wie die Warteschlange "effizient" gelöscht werden kann. Ich gehe also davon aus, dass er eine bessere Komplexität als lineares O (Warteschlangengröße) will. Methoden, die Sie bedienten, haben die gleiche Komplexität: Gemäß stl-Referenz hat operator = Komplexität O (Warteschlangengröße). IMHO ist darauf zurückzuführen, dass jedes Element der Warteschlange separat reserviert ist und nicht wie in vector in einem großen Speicherblock zugewiesen wird. Um den gesamten Speicher zu löschen, müssen wir jedes Element separat löschen. Der einfachste Weg, stl::queue zu löschen, ist eine Zeile:

while(!Q.empty()) Q.pop();
18
janis

Offensichtlich gibt es zwei naheliegendste Möglichkeiten, std::queue zu löschen: mit einem leeren Objekt zu vertauschen und einem leeren Objekt zuzuordnen.

Ich würde vorschlagen, die Zuweisung zu verwenden, weil sie einfach schneller, lesbarer und eindeutiger ist.

Ich habe die Leistung anhand des folgenden einfachen Codes gemessen und fand heraus, dass das Austauschen in der C++ 03-Version 70-80% langsamer ist als die Zuweisung zu einem leeren Objekt. In C++ 11 gibt es jedoch keinen Leistungsunterschied. Jedenfalls würde ich mit der Aufgabe gehen.

#include <algorithm>
#include <ctime>
#include <iostream>
#include <queue>
#include <vector>

int main()
{
    std::cout << "Started" << std::endl;

    std::queue<int> q;

    for (int i = 0; i < 10000; ++i)
    {
        q.Push(i);
    }

    std::vector<std::queue<int> > queues(10000, q);

    const std::clock_t begin = std::clock();

    for (std::vector<int>::size_type i = 0; i < queues.size(); ++i)
    {
        // OK in all versions
        queues[i] = std::queue<int>();

        // OK since C++11
        // std::queue<int>().swap(queues[i]);

        // OK before C++11 but slow
        // std::queue<int> empty;
        // std::swap(empty, queues[i]);
    }

    const double elapsed = double(clock() - begin) / CLOCKS_PER_SEC;

    std::cout << elapsed << std::endl;

    return 0;
}
12
tim

In C++ 11 können Sie die Warteschlange folgendermaßen löschen:

std::queue<int> queue;
// ...
queue = {};
4
kolage

Sie können eine Klasse erstellen, die von der Warteschlange erbt, und den zugrunde liegenden Container direkt löschen. Das ist sehr effizient.

template<class T>
class queue_clearable : public std::queue<T>
{
public:
    void clear()
    {
        c.clear();
    }
};

Möglicherweise erlaubt Ihre Implementierung auch Ihrem Queue-Objekt (hier JobQueue), std::queue<Job> zu erben, anstatt die Queue als Member-Variable zu haben. Auf diese Weise hätten Sie in Ihren Mitgliedsfunktionen direkten Zugriff auf c.clear().

3
typ1232

Ich möchte mich nicht auf swap() verlassen oder die Warteschlange auf ein neu erstelltes Warteschlangenobjekt setzen, da die Elemente der Warteschlange nicht ordnungsgemäß zerstört werden. Durch Aufruf von pop() wird der Destruktor für das jeweilige Elementobjekt aufgerufen. Dies ist möglicherweise kein Problem in <int>-Warteschlangen, kann aber durchaus Nebenwirkungen auf Warteschlangen mit Objekten haben.

Daher ist eine Schleife mit while(!queue.empty()) queue.pop(); leider zumindest für Warteschlangen, die Objekte enthalten, die effizienteste Lösung, wenn Sie mögliche Nebenwirkungen vermeiden möchten.

1
Marste

Angenommen, Ihr m_Queue enthält Ganzzahlen:

std::queue<int>().swap(m_Queue)

Ansonsten, wenn es z. Zeiger auf Job-Objekte, dann:

std::queue<Job*>().swap(m_Queue)

Auf diese Weise tauschen Sie eine leere Warteschlange mit Ihrem m_Queue aus, wodurch m_Queue leer wird.

Eine andere Option besteht darin, einen einfachen Hack zu verwenden, um den zugrunde liegenden Container std::queue::c abzurufen und clear aufzurufen. Dieser Member muss standardmäßig in std::queue vorhanden sein, ist jedoch leider protected. Der Hack hier wurde von dieser Antwort übernommen.

#include <queue>

template<class ADAPTER>
typename ADAPTER::container_type& get_container(ADAPTER& a)
{
    struct hack : ADAPTER
    {
        static typename ADAPTER::container_type& get(ADAPTER& a)
        {
            return a .* &hack::c;
        }
    };
    return hack::get(a);
}

template<typename T, typename C>
void clear(std::queue<T,C>& q)
{
    get_container(q).clear();
}

#include <iostream>
int main()
{
    std::queue<int> q;
    q.Push(3);
    q.Push(5);
    std::cout << q.size() << '\n';
    clear(q);
    std::cout << q.size() << '\n';
}
0
Ruslan

Ich mache das (mit C++ 14):

std::queue<int> myqueue;
myqueue = decltype(myqueue){};

Diese Methode ist nützlich, wenn Sie einen nicht trivialen Warteschlangentyp haben, für den Sie keinen Alias ​​/ Typedef erstellen möchten. Ich sorge immer dafür, dass Sie einen Kommentar zu dieser Verwendung hinterlassen, um den ahnungslosen Programmierern zu erklären, dass dies nicht verrückt ist und anstelle einer tatsächlichen clear()-Methode durchgeführt wird.

0
void.pointer

Die Verwendung eines unique_ptr ist möglicherweise in Ordnung.
Sie setzen ihn dann zurück, um eine leere Warteschlange zu erhalten, und geben Sie den Speicher der ersten Warteschlange frei. Ich bin nicht sicher - aber ich denke, es ist O (1).

Möglicher Code:

typedef queue<int> quint;

unique_ptr<quint> p(new quint);

// ...

p.reset(new quint);  // the old queue has been destroyed and you start afresh with an empty queue
0
Ronnie