如何有效清除std :: queue?

aJ.*_*aJ. 159 c++ queue stl

我正在使用std :: queue来实现JobQueue类.(基本上这个类以FIFO方式处理每个作业).在一种情况下,我想一次性清除队列(从队列中删除所有作业).我没有看到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();
    }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*eas 246

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

void clear( std::queue<int> &q )
{
   std::queue<int> empty;
   std::swap( q, empty );
}
Run Code Online (Sandbox Code Playgroud)

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

  • 更好的是`std :: queue <int>().swap(q)`.使用复制和交换习惯用法,所有这些都应该等同于`q = std :: queue <int>()`. (39认同)
  • 虽然`std :: queue <int>().swap(q)`等同于上面的代码,但`q = std :: queue <int>()`不一定是等价的.由于在分配的内存的分配中没有所有权的转移,一些容器(如vector)可能只调用先前保持的元素的析构函数并设置*size*(或使用存储的指针的等效操作)而不实际释放内存. (10认同)
  • @ThorbjørnLindeijer:从原始队列的用户的角度来看,这些元素不存在.你是正确的,因为它们将被一个接一个地销毁并且成本是线性的,但是除了本地函数之外的任何其他人都无法访问它们.在多线程环境中,您将锁定,将非临时队列与原始队列交换,解锁(以允许并发访问)并让交换的队列死亡.这样,您可以在关键部分之外移动销毁成本. (10认同)
  • `queue`没有`swap(other)`方法,所以`queue <int>().swap(q)`不编译.我认为你必须使用通用的`swap(a,b)`. (6认同)
  • @ThorbjørnLindeijer:在C++ 03中你是对的,在C++ 11队列中有*swap*作为成员函数,另外还有一个自由函数重载,它将交换两个相同类型的队列. (3认同)

小智 42

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

#include <queue>
using namespace std;;

int main() {
    queue <int> q1;
    // stuff
    q1 = queue<int>();  
}
Run Code Online (Sandbox Code Playgroud)

  • 使用新的C++,只需`q1 = {}`即可 (25认同)
  • `q1 = queue <int>();`既短又清楚(你不是_really_试图`.swap`,你试图`.clear`). (11认同)
  • @Naszta请详细说明`swap`是如何"更有效" (8认同)
  • @Ari语法(2)在[list_initialization](https://en.cppreference.com/w/cpp/language/list_initialization)和(10)在[operator_assignment](https://en.cppreference.com/w/ cpp /语言/ operator_assignment)。默认的queue &lt;T&gt;构造函数与空参数列表`{}`匹配并且是隐式的,因此被调用,然后`q1.operator =(queue &lt;T&gt; &amp;&amp;)`消耗新创建的`queue`。 (2认同)

小智 23

'David Rodriguez','anon'该主题的作者询问如何"有效地"清除队列,因此我认为他希望复杂性高于线性O(队列大小).您提供的方法具有相同的复杂性:根据stl引用,operator =具有复杂度O(队列大小).恕我直言,因为队列的每个元素都是单独保留的,并且它不会分配在一个大的内存块中,就像在向量中一样.因此,为了清除所有内存,我们必须分别删除每个元素.所以最明确的方法operator =是一行:

while(!Q.empty()) Q.pop();
Run Code Online (Sandbox Code Playgroud)

  • 如果您使用实际数据,则不能只查看操作的O复杂性.如果线性操作的常数使得它比所有的"n <2 ^ 64"的二次方程慢,我会采用"O(n ^ 2)"算法而不是"O(n)"算法,除非我有一些强大的有理由相信我必须搜索IPv6地址空间或其他一些特定问题.现实中的表现对我来说比对极限的表现更重要. (4认同)
  • 这个答案比公认的答案更好,因为内部队列在被销毁时仍会这样做。因此,可接受的答案是O(n),它还会对全新队列进行额外的分配和初始化。 (2认同)

tim*_*tim 15

显然,有两种最明显的方法可以清除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;
}
Run Code Online (Sandbox Code Playgroud)

  • C++11 的结果:`queues[i] = std::queue&lt;int&gt;();`:1.168,`std::queue&lt;int&gt;().swap(queues[i]);`:1.151, `std::queue&lt;int&gt; 空;std::swap(空,queues[i]);`:1.164,`while (!queues[i].empty())queues[i].pop();`:0.189。最后一个是迄今为止最快的。感谢您提供测试代码! (2认同)

kol*_*age 6

在C ++ 11中,您可以通过执行以下操作清除队列:

std::queue<int> queue;
// ...
queue = {};
Run Code Online (Sandbox Code Playgroud)

  • 嘿我只是想知道它是 O(n) 还是 O(1) ? (6认同)

typ*_*232 5

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

template<class T>
class queue_clearable : public std::queue<T>
{
public:
    void clear()
    {
        c.clear();
    }
};
Run Code Online (Sandbox Code Playgroud)

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

  • STL 容器不是为了继承而设计的。在这种情况下,您可能没问题,因为您没有添加任何额外的成员变量,但这通常不是一件好事。 (12认同)