Я использую std :: queue для реализации класса JobQueue. (В основном этот класс обрабатывает каждую работу в порядке FIFO). В одном сценарии я хочу очистить очередь за один раз (удалить все задания из очереди). Я не вижу четкого метода, доступного в классе std :: queue.
Как эффективно реализовать метод clear для класса JobQueue?
У меня есть одно простое решение выскочить в цикле, но я ищу лучшие способы.
//Clears the job queue
void JobQueue ::clearJobs()
{
// I want to avoid pop in a loop
while (!m_Queue.empty())
{
m_Queue.pop();
}
}
deque
поддерживает ясноОтветы:
Распространенная идиома для очистки стандартных контейнеров - это замена пустой версией контейнера:
Это также единственный способ очистки памяти, содержащейся в некоторых контейнерах (std :: vector).
источник
std::queue<int>().swap(q)
. С идиом copy и swap все это должно быть эквивалентноq = std::queue<int>()
.std::queue<int>().swap(q)
это эквивалентно приведенному выше коду, оноq = std::queue<int>()
не обязательно должно быть эквивалентным. Поскольку при назначении выделенной памяти нет передачи прав собственности, некоторые контейнеры (например, вектор) могут просто вызывать деструкторы ранее удерживаемых элементов и устанавливать размер (или эквивалентную операцию с сохраненными указателями) без фактического освобождения памяти.queue
не имеетswap(other)
метода, поэтомуqueue<int>().swap(q)
не компилируется. Я думаю, что вы должны использовать общийswap(a, b)
.Да - небольшая ошибка в классе очереди, ИМХО. Вот что я делаю:
источник
swap
это «более эффективно»q1.swap(queue<int>());
q1=queue<int>();
и короче, и четче (вы на самом деле не пытаетесь.swap
, вы пытаетесь.clear
).q1 = {}
достаточноqueue<T>
конструктора соответствует пустому списку аргументов{}
и неявно, так это называется, а затемq1.operator=(queue<T>&&)
уничтожает вновь созданныйqueue
Автор темы спросил, как очистить очередь «эффективно», поэтому я предполагаю, что он хочет лучшей сложности, чем линейный O (размер очереди) . Методы , обслуживаемый Дэвид Родригес , Anon имеют одинаковую сложность: в соответствии со справочной STL,
operator =
имеет сложность O (очередь размера) . ИМХО, это потому, что каждый элемент очереди зарезервирован отдельно и не размещается в одном большом блоке памяти, как в векторе. Таким образом, чтобы очистить всю память, мы должны удалить каждый элемент отдельно. Таким образом, самый простой способ очиститьstd::queue
это одна строка:источник
O(n^2)
алгоритм надO(n)
алгоритмом, если бы константы линейной операции делали его медленнее, чем квадратичный для всехn < 2^64
, если у меня не было веской причины полагать, что мне пришлось искать в адресном пространстве IPv6 или какой-то другой конкретной проблеме. Производительность на самом деле важнее для меня, чем производительность на пределе.Очевидно, есть два наиболее очевидных способа очистки
std::queue
: замена пустого объекта и присвоение пустому объекту.Я бы предложил использовать назначение, потому что оно просто быстрее, более читабельно и однозначно.
Я измерил производительность с помощью следующего простого кода и обнаружил, что подкачка в версии C ++ 03 работает на 70-80% медленнее, чем присвоение пустому объекту. Однако в C ++ 11 нет различий в производительности. Во всяком случае, я бы пошел с заданием.
источник
В C ++ 11 вы можете очистить очередь, выполнив это:
источник
Вы можете создать класс, который наследует от очереди, и очистить базовый контейнер напрямую. Это очень эффективно.
Возможно, ваша реализация также позволяет вашему объекту Queue (здесь
JobQueue
) наследоватьstd::queue<Job>
вместо того, чтобы иметь очередь в качестве переменной-члена. Таким образом, у вас будет прямой доступ кc.clear()
функциям вашего члена.источник
Предполагая, что ваш
m_Queue
содержит целые числа:В противном случае, если он содержит, например, указатели на
Job
объекты, то:Таким образом, вы меняете пустую очередь на свою
m_Queue
, таким образом,m_Queue
становитесь пустыми.источник
Я бы предпочел не полагаться
swap()
или устанавливать очередь для вновь созданного объекта очереди, потому что элементы очереди не уничтожаются должным образом. Вызовpop()
вызывает деструктор для соответствующего объекта элемента. Это может не быть проблемой в<int>
очередях, но вполне может иметь побочные эффекты для очередей, содержащих объекты.Поэтому
while(!queue.empty()) queue.pop();
, к сожалению, цикл с представляется наиболее эффективным решением, по крайней мере, для очередей, содержащих объекты, если вы хотите предотвратить возможные побочные эффекты.источник
swap()
или присваивание вызывает деструктор в теперь не существующей очереди, который вызывает деструкторы всех объектов в очереди. Теперь, если в вашей очереди есть объекты, которые на самом деле являются указателями, это другая проблема - но простоеpop()
тоже вам там не поможет.Я делаю это (используя C ++ 14):
Этот способ полезен, если у вас нетривиальный тип очереди, для которого вы не хотите создавать псевдоним / typedef. Тем не менее, я всегда оставляю комментарий об этом использовании, чтобы объяснить ничего не подозревающим программистам, что это не сумасшествие и сделано вместо реального
clear()
метода.источник
myqueue = { };
будет работать просто отлично.Использование
unique_ptr
может быть в порядке.Затем вы сбрасываете его, чтобы получить пустую очередь и освободить память первой очереди. Что касается сложности? Я не уверен - но думаю, это O (1).
Возможный код:
источник
Другой вариант - использовать простой хак, чтобы получить базовый контейнер
std::queue::c
и вызватьclear
его. Этот участник должен присутствовать вstd::queue
соответствии со стандартом, но к сожалениюprotected
. Взлом здесь был взят из этого ответа .источник