Как эффективно очистить std:: queue?

Я использую std:: queue для реализации класса JobQueue. (В основном этот класс обрабатывает каждое задание в режиме FIFO). В одном сценарии я хочу очистить очередь одним выстрелом (удалить все задания из очереди). Я не вижу никакого ясного метода, доступного в классе std:: queue.

Как эффективно реализовать прозрачный метод для класса JobQueue?

У меня есть одно простое решение в цикле, но я ищу лучшие способы.

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

Обычная идиома для очистки стандартных контейнеров заменяется пустой версией контейнера:

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

Это также единственный способ фактически очистить память, хранящуюся внутри некоторых контейнеров (std::vector)

217
ответ дан 02 апр. '09 в 13:23
источник

Да - немного недостоверности класса очереди, ИМХО. Это то, что я делаю:

#include <queue>
using namespace std;;

int main() {
    queue <int> q1;
    // stuff
    q1 = queue<int>();  
}
39
ответ дан 02 апр. '09 в 13:19
источник

'Дэвид Родригес', 'anon' Автор темы спросил, как очистить очередь "эффективно", поэтому я предполагаю, что ему нужна более сложная задача, чем линейная O (размер очереди). Методы, которые вы обслуживали, имеют одинаковую сложность: в соответствии со ссылкой stl оператор = имеет сложность O (размер очереди). ИМХО это потому, что каждый элемент очереди зарезервирован отдельно и не выделяется в одном большом блоке памяти, как в векторе. Чтобы очистить всю память, мы должны удалить каждый элемент отдельно. Таким образом, самый прямой способ очистки stl::queue - это одна строка:

while(!Q.empty()) Q.pop();
18
ответ дан 30 авг. '10 в 14:12
источник

По-видимому, существует два наиболее очевидных способа очистки std::queue: замена с пустым объектом и назначение пустого объекта.

Я бы предложил использовать назначение, потому что он просто быстрее, читабельнее и недвусмысленно.

Я измерил производительность, используя следующий простой код, и я обнаружил, что обмен в версии С++ 03 работает на 70-80% медленнее, чем назначение пустого объекта. Однако в С++ 11 нет никакой разницы в производительности. Во всяком случае, я бы пошел с назначением.

#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;
}
11
ответ дан 25 февр. '15 в 19:54
источник

В С++ 11 вы можете очистить очередь, выполнив это:

std::queue<int> queue;
// ...
queue = {};
4
ответ дан 23 сент. '16 в 11:19
источник

Вы можете создать класс, который наследует от очереди, и сразу очистить базовый контейнер. Это очень эффективно.

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

Возможно, ваша реализация также позволяет вашему объекту Queue (здесь JobQueue) наследовать std::queue<Job> вместо очереди в качестве переменной-члена. Таким образом, у вас будет прямой доступ к c.clear() в ваших функциях-членах.

3
ответ дан 29 апр. '13 в 21:57
источник

Я бы предпочел не полагаться на swap() или устанавливать очередь на вновь созданный объект очереди, потому что элементы очереди не были должным образом уничтожены. Вызов pop() вызывает деструктор для соответствующего элемента элемента. Возможно, это не проблема в очередях <int>, но может иметь побочные эффекты для очередей, содержащих объекты.

Поэтому цикл с while(!queue.empty()) queue.pop();, к сожалению, является наиболее эффективным решением, по крайней мере, для очередей, содержащих объекты, если вы хотите предотвратить возможные побочные эффекты.

1
ответ дан 15 февр. '14 в 1:03
источник

Предполагая, что ваш m_Queue содержит целые числа:

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

В противном случае, если он содержит, например, указатели на объекты Job, то:

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

Таким образом, вы m_Queue пустую очередь на m_Queue, поэтому m_Queue становится пустым.

1
ответ дан 26 июля '18 в 12:53
источник

Использование unique_ptr может быть в порядке.
Затем вы получите reset, чтобы получить пустую очередь и освободить память первой очереди. Что касается сложности? Я не уверен, но думаю, что это O (1).

Возможный код:

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
ответ дан 10 янв. '15 в 20:38
источник

Я делаю это (используя С++ 14):

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

Этот способ полезен, если у вас нетривиальный тип очереди, для которого вы не хотите создавать псевдоним /typedef. Тем не менее, я всегда оставляю комментарий об этом использовании, чтобы объяснить ничего не подозревающим/обслуживающим программистам, что это не сумасшествие, а сделано вместо реального метода clear().

0
ответ дан 24 сент. '18 в 19:16
источник

Другим вариантом является использование простого хака для получения базового контейнера std::queue::c и вызова clear для него. Этот член должен присутствовать в std::queue согласно стандарту, но, к сожалению, protected. Взлом здесь был взят из этого ответа.

#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
ответ дан 14 дек. '18 в 17:39
источник