JobQueueクラスの実装にstd :: queueを使用しています。 (基本的に、このクラスは各ジョブをFIFO方式で処理します)。あるシナリオでは、1回でキューをクリアしたい(キューからすべてのジョブを削除する)。 std :: queueクラスで利用できる明確なメソッドがありません。
JobQueueクラスのclearメソッドを効率的に実装するにはどうすればよいですか?
ループ内にポップする簡単なソリューションが1つありますが、より良い方法を探しています。
//Clears the job queue
void JobQueue ::clearJobs()
{
// I want to avoid pop in a loop
while (!m_Queue.empty())
{
m_Queue.pop();
}
}
標準コンテナをクリアする一般的なイディオムは、コンテナの空のバージョンと交換することです:
void clear( std::queue<int> &q )
{
std::queue<int> empty;
std::swap( q, empty );
}
いくつかのコンテナ(std :: vector)内に保持されているメモリを実際にクリアする唯一の方法でもあります
はい-キュークラス、IMHOのちょっとした機能不全。これが私がすることです:
#include <queue>
using namespace std;;
int main() {
queue <int> q1;
// stuff
q1 = queue<int>();
}
トピックの作成者は、「効率的に」キューをクリアする方法を尋ねたので、線形O(キューサイズ)よりも複雑なものが欲しいと思います。 David Rodriguez 、 anon が提供するメソッドは同じ複雑さを持ちます。STLリファレンスによれば、operator =
は複雑さO(queueサイズ)。これは、キューの各要素が個別に予約されており、ベクターのように1つの大きなメモリブロックに割り当てられていないためです。したがって、すべてのメモリをクリアするには、すべての要素を個別に削除する必要があります。したがって、std::queue
をクリアする最も簡単な方法は1行です。
while(!Q.empty()) Q.pop();
明らかに、std::queue
をクリアする最も明白な方法は2つあります。空のオブジェクトとの交換と空のオブジェクトへの割り当てです。
割り当てを使用することをお勧めします。それは、より速く、読みやすく、明確であるためです。
次の簡単なコードを使用してパフォーマンスを測定したところ、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;
}
C++ 11では、これを行うことでキューをクリアできます。
std::queue<int> queue;
// ...
queue = {};
キューから継承するクラスを作成し、基礎となるコンテナを直接クリアできます。これは非常に効率的です。
template<class T>
class queue_clearable : public std::queue<T>
{
public:
void clear()
{
c.clear();
}
};
実装によっては、キューをメンバー変数として持つ代わりに、Queueオブジェクト(ここではJobQueue
)がstd::queue<Job>
を継承することもできます。これにより、メンバー関数のc.clear()
に直接アクセスできます。
m_Queue
に整数が含まれていると仮定します:
std::queue<int>().swap(m_Queue)
それ以外の場合、たとえばJob
オブジェクトへのポインター、次に:
std::queue<Job*>().swap(m_Queue)
この方法では、空のキューをm_Queue
と交換するため、m_Queue
は空になります。
私はこれを行います(C++ 14を使用):
std::queue<int> myqueue;
myqueue = decltype(myqueue){};
この方法は、エイリアス/ typedefを作成したくない自明でないキュータイプがある場合に便利です。しかし、この使用法についてはコメントを残し、疑いのない/メンテナンスプログラマーに、これがクレイジーではなく、実際のclear()
メソッドの代わりに行われることを説明します。
キュー要素が適切に破棄されないため、swap()
に依存したり、新しく作成されたキューオブジェクトにキューを設定したりする必要はありません。 pop()
を呼び出すと、それぞれの要素オブジェクトのデストラクタが呼び出されます。これは<int>
キューでは問題にならないかもしれませんが、オブジェクトを含むキューに非常によく副作用があるかもしれません。
したがって、可能性のある副作用を防ぎたい場合は、残念ながら、while(!queue.empty()) queue.pop();
を含むループが、少なくともオブジェクトを含むキューに対して最も効率的なソリューションであるように見えます。
もう1つのオプションは、単純なハックを使用して、基になるコンテナstd::queue::c
を取得し、そのコンテナでclear
を呼び出すことです。このメンバーは、標準に従ってstd::queue
に存在する必要がありますが、残念ながらprotected
です。ここでのハックは this answer から取ったものです。
#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';
}
unique_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