web-dev-qa-db-fra.com

Comment effacer la std :: queue efficacement?

J'utilise std :: queue pour l'implémentation de la classe JobQueue. (En gros, cette classe traite chaque travail de la manière FIFO]. méthode clear disponible dans la classe std :: queue.

Comment implémenter efficacement la méthode clear pour la classe JobQueue?

J'ai une solution simple de sauter dans une boucle mais je cherche de meilleurs moyens.

//Clears the job queue
void JobQueue ::clearJobs()
 {
  // I want to avoid pop in a loop
    while (!m_Queue.empty())
    {
        m_Queue.pop();
    }
}
140
aJ.

Un idiome courant pour effacer les conteneurs standard est l’échange avec une version vide du conteneur:

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

C’est aussi le seul moyen de vider la mémoire de certains conteneurs (std :: vector)

Oui - un peu d'un mauvais fonctionnement de la classe de file d'attente, à mon humble avis. C'est ce que je fais:

#include <queue>
using namespace std;;

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

'David Rodriguez', 'anon' L'auteur du sujet a demandé comment effacer la file d'attente "efficacement", je suppose donc qu'il souhaite une plus grande complexité que le linéaire (taille de la file d'attente). Les méthodes que vous avez utilisées ont la même complexité: En fonction de la référence stl, operator = a la complexité O (taille de la file d'attente). IMHO c'est parce que chaque élément de la file d'attente est réservé séparément et il n'est pas alloué dans un gros bloc de mémoire, comme dans le vecteur. Donc, pour effacer toute la mémoire, nous devons supprimer chaque élément séparément. Donc, le moyen le plus direct pour effacer stl::queue est une ligne:

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

Apparemment, il existe deux méthodes les plus évidentes pour effacer std::queue: permuter avec un objet vide et attribuer à un objet vide.

Je suggérerais d'utiliser une affectation parce qu'elle est simplement plus rapide, plus lisible et sans ambiguïté.

J'ai mesuré les performances à l'aide du code simple suivant et j'ai constaté que la permutation dans la version C++ 03 fonctionne 70 à 80% plus lentement que l'affectation à un objet vide. En C++ 11, il n'y a pas de différence de performances. Quoi qu'il en soit, j'irais avec cession.

#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

En C++ 11, vous pouvez effacer la file d'attente en procédant comme suit:

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

Vous pouvez créer une classe qui hérite de la file d'attente et effacer directement le conteneur sous-jacent. C'est très efficace.

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

Peut-être que votre implémentation permet également à votre objet Queue (ici JobQueue) d'hériter std::queue<Job> au lieu d'avoir la file d'attente comme variable membre. De cette façon, vous auriez un accès direct à c.clear() dans vos fonctions membres.

3
typ1232

Je préfère ne pas compter sur swap() ou définir la file d'attente sur un objet de file d'attente nouvellement créé, car les éléments de la file d'attente ne sont pas détruits correctement. L'appel de pop()invoque le destructeur pour l'objet élément correspondant. Cela pourrait ne pas être un problème dans les files d'attente <int>, mais pourrait très bien avoir des effets secondaires sur les files d'attente contenant des objets.

Par conséquent, une boucle avec while(!queue.empty()) queue.pop(); semble malheureusement être la solution la plus efficace, du moins pour les files d'attente contenant des objets, si vous souhaitez éviter les éventuels effets secondaires.

1
Marste

En supposant que votre m_Queue contient des entiers:

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

Sinon, si elle contient par exemple pointeurs vers les objets Job, puis:

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

De cette façon, vous échangez une file vide avec votre m_Queue. Ainsi, m_Queue devient vide.

1

Une autre option consiste à utiliser un hack simple pour obtenir le conteneur sous-jacent std::queue::c et appeler clear. Ce membre doit être présent dans std::queue conformément à la norme, mais est malheureusement protected. Le piratage ici a été pris de cette réponse .

#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

Je fais cela (en utilisant C++ 14):

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

Cette méthode est utile si vous avez un type de file d'attente non trivial pour lequel vous ne voulez pas créer d'alias/typedef. Je m'assure toutefois toujours de laisser un commentaire sur cette utilisation, cependant, pour expliquer aux programmeurs peu méfiants/de maintenance que ce n'est pas fou, et fait à la place d'une méthode clear().

0
void.pointer

Utiliser un unique_ptr pourrait être OK.
Vous le réinitialisez ensuite pour obtenir une file vide et libérez la mémoire de la première file ..__ Quant à la complexité? Je ne suis pas sûr - mais suppose que c'est O (1).

Code possible:

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