189

我正在使用 std::queue 来实现 JobQueue 类。(基本上这个类以先进先出的方式处理每个作业)。在一种情况下,我想一次性清除队列(从队列中删除所有作业)。我在 std::queue 类中看不到任何可用的明确方法。

如何有效地实现 JobQueue 类的 clear 方法?

我有一个简单的循环弹出解决方案,但我正在寻找更好的方法。

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

11 回答 11

290

清除标准容器的一个常见习惯用法是使用空版本的容器进行交换:

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

这也是实际清除某些容器内的内存的唯一方法(std::vector)

于 2009-04-02T10:23:56.143 回答
53

是的 - 恕我直言,队列类有点错误。这就是我所做的:

#include <queue>
using namespace std;;

int main() {
    queue <int> q1;
    // stuff
    q1 = queue<int>();  
}
于 2009-04-02T10:19:44.267 回答
33

该主题的作者询问如何“有效地”清除队列,所以我认为他想要比线性O(queue size)更好的复杂性。David Rodriguez服务的方法,anon具有相同的复杂性:根据 STL 参考,operator =具有复杂性O(queue size)。恕我直言,这是因为队列的每个元素都是单独保留的,并且不像向量中那样分配在一个大内存块中。所以要清除所有内存,我们必须单独删除每个元素。所以最直接的清除方法std::queue是一行:

while(!Q.empty()) Q.pop();
于 2010-08-30T11:12:42.053 回答
19

显然,有两种最明显的清除方法std::queue:与空对象交换和分配给空对象。

我建议使用赋值,因为它更快、更易读、更明确。

我使用以下简单代码测量了性能,发现在 C++03 版本中交换比分配给空对象慢 70-80%。但是,在 C++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;
}
于 2015-02-25T16:54:05.563 回答
18

在 C++11 中,您可以通过以下方式清除队列:

std::queue<int> queue;
// ...
queue = {};
于 2016-09-23T08:19:52.340 回答
5

您可以创建一个从队列继承的类并直接清除底层容器。这是非常有效的。

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

也许您的实现还允许您的 Queue 对象(此处JobQueue)继承std::queue<Job>而不是将队列作为成员变量。这样您就可以直接访问c.clear()您的成员函数。

于 2013-04-29T18:57:22.763 回答
3

假设您m_Queue包含整数:

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

否则,如果它包含例如指向Job对象的指针,则:

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

这样,您将一个空队列与您的 交换m_Queue,因此m_Queue变为空。

于 2018-07-26T09:53:49.930 回答
2

我宁愿不依赖swap()或将队列设置为新创建的队列对象,因为队列元素没有正确销毁。调用pop()会调用相应元素对象的析构函数。这在队列中可能不是问题,<int>但很可能对包含对象的队列产生副作用。

while(!queue.empty()) queue.pop();因此,不幸的是,如果您想防止可能的副作用,至少对于包含对象的队列来说,循环似乎是最有效的解决方案。

于 2014-02-14T22:03:49.060 回答
2

我这样做(使用 C++14):

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

如果您有一个不想为其构建别名/类型定义的非平凡队列类型,这种方法很有用。不过,我总是确保对这种用法发表评论,以向毫无戒心的/维护程序员解释这并不疯狂,并且代替了实际clear()方法。

于 2018-09-24T16:16:00.577 回答
1

使用 aunique_ptr可能没问题。
然后重置它以获得一个空队列并释放第一个队列的内存。至于复杂度?我不确定 - 但猜它是 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
于 2015-01-10T17:38:07.543 回答
1

另一种选择是使用简单的 hack 来获取底层容器std::queue::c并调用clear它。该成员必须按照std::queue标准存在,但不幸的是protected。这里的hack取自这个答案

#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';
}
于 2018-12-14T14:39:26.760 回答