std :: queueを効率的にクリアするにはどうすればよいですか?


166

JobQueueクラスの実装にstd :: queueを使用しています。(基本的に、このクラスはFIFO方式で各ジョブを処理します)。1つのシナリオでは、キューを1回でクリアします(キューからすべてのジョブを削除します)。std :: queueクラスで使用できるclearメソッドがありません。

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();
    }
}

回答:


257

標準コンテナをクリアするための一般的なイディオムは、空のバージョンのコンテナと交換することです。

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

また、一部のコンテナ(std :: vector)内に保持されているメモリを実際にクリアする唯一の方法でもあります。


41
まだ良いですstd::queue<int>().swap(q)。コピーとスワップのイディオムを使用すると、これらはすべてと同等になりq = std::queue<int>()ます。
Alexandre C.

12
一方でstd::queue<int>().swap(q)上記のコードと同等です、q = std::queue<int>()必要性と同等ではありません。割り当てられたメモリの割り当てには所有権の譲渡がないため、一部のコンテナ(ベクターなど)は、実際にメモリを解放せずに、以前に保持された要素のデストラクタを呼び出してサイズ(または格納されたポインタを使用した同等の操作)を設定するだけです。
DavidRodríguez-

6
queue持っていないswap(other)方法を、そうqueue<int>().swap(q)コンパイルされません。ジェネリックを使わないといけないと思いますswap(a, b)
Dustin Boswell、2011年

3
@ThorbjørnLindeijer:そうです、C ++ 03では、C ++ 11では、キューにはメンバー関数としてスワップがあり、さらに、同じタイプの2つのキューをスワップするフリー関数オーバーロードがあります。
デビッドロドリゲス-dribeas

10
@ThorbjørnLindeijer:元のキューのユーザーの観点から、これらの要素は存在しません。あなたはそれらが次々に破壊され、コストが線形であるという点で正しいですが、それらはローカル関数以外の誰もアクセスできません。マルチスレッド環境では、非一時的なキューをロックし、元のキューと交換し、ロックを解除して(同時アクセスを可能にするため)、交換したキューを停止させます。このようにして、破壊のコストをクリティカルセクションの外に移動できます。
デビッドロドリゲス-dribeas

46

はい-キュークラスのちょっとした機能の誤解です。これが私がすることです:

#include <queue>
using namespace std;;

int main() {
    queue <int> q1;
    // stuff
    q1 = queue<int>();  
}

8
@Naszta swap「より効果的」な方法について詳しく説明してください
bobobobo 2013年

@bobobobo:q1.swap(queue<int>());
Naszta 2013年

12
q1=queue<int>();どちらも短く、明確です(実際にを試みているのではなく.swap、を試みています.clear)。
bobobobo 2013年

28
新しいC ++では、q1 = {}
それで

2
@Ari構文(2)at list_initializationおよび(10)at operator_assignment。デフォルトのqueue<T>コンストラクターは空の引数リスト{}と一致して暗黙的であるため、呼び出され、q1.operator=(queue<T>&&)新しく作成されたものを消費しますqueue
Mykola Bogdiuk

26

トピックの作者はキューを「効率的に」クリアする方法を尋ねたので、彼は線形O(キューサイズ)よりも複雑さを望んでいると思います。提供される方法デビッド・ロドリゲスはアノンは同じ複雑さを持っている:STLのリファレンスによると、operator =複雑さがあるO(キューサイズ)。これは、キューの各要素が個別に予約されており、ベクトルのように1つの大きなメモリブロックに割り当てられていないためです。したがって、すべてのメモリをクリアするには、すべての要素を個別に削除する必要があります。したがって、クリアする最も簡単な方法std::queueは1行です。

while(!Q.empty()) Q.pop();

5
実際のデータを操作している場合、操作のOの複雑さだけを見ることはできません。IPv6アドレススペースやその他の特定の問題を検索する必要があると確信する何らかの強い理由がない限り、線形演算の定数によってallの2次よりも遅くなる場合は、O(n^2)アルゴリズムよりアルゴリズムを採用しO(n)ますn < 2^64。私にとっては、限界性能よりも実際の性能のほうが重要です。
David Stone、

2
これは、受け入れられた回答よりも優れた回答です。内部でキューに入れられると、破棄されたときにこれが行われます。したがって、受け入れられる答えはO(n)に加えて、新しいキューの追加の割り当てと初期化を行います。
Shital Shah、

O(n)がn以下の複雑さを意味することを覚えておいてください。したがって、はい、queue <vector <int >>のように場合によっては、各要素を1つずつ破棄する必要がありますが、いずれの場合も遅くなりますが、queue <int>では、メモリは実際には1つの大きなブロックに割り当てられます。したがって、内部要素を破棄する必要がないため、キューのデストラクタは、ほぼ確実にO(n)時間よりも短い単一の効率的なfree()操作を使用できます。
ベンジャミン

15

どうやら、明らかにする2つの最も明白な方法があります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;
}

8

C ++ 11では、次のようにしてキューをクリアできます。

std::queue<int> queue;
// ...
queue = {};

4

キューから継承するクラスを作成し、基になるコンテナーを直接クリアできます。これは非常に効率的です。

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

実装によっては、キューをメンバー変数として持つ代わりに、Queueオブジェクト(ここではJobQueue)が継承することもできますstd::queue<Job>。これによりc.clear()、メンバー関数で直接アクセスできます。


9
STLコンテナは、継承元として設計されていません。この場合、追加のメンバー変数を追加しないので、おそらく大丈夫ですが、一般的に行うのは良いことではありません。
bstamour 2013

2

あなたのm_Queue整数が含まれていると仮定します:

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

それ以外の場合、たとえばJobオブジェクトへのポインタが含まれている場合は、次のようになります。

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

あなたと空のキューを交換この方法ではm_Queue、このようにm_Queue空になります。


1

swap()キューの要素が適切に破棄されないため、キューを新しく作成したキューオブジェクトに依存したり設定したりするのではなく、呼び出すpop()と、それぞれの要素オブジェクトのデストラクタが呼び出されます。これは<int>キューでは問題にならない可能性がありますが、オブジェクトを含むキューに副作用がある可能性があります。

したがって、のループはwhile(!queue.empty()) queue.pop();、起こり得る副作用を防ぎたい場合、少なくともオブジェクトを含むキューにとって、残念ながら最も効率的な解決策のようです。


3
swap()または割り当てにより、キューが破棄されたキューのデストラクタが呼び出され、キューのすべてのオブジェクトのデストラクタが呼び出されます。さて、キューに実際にポインタであるオブジェクトがある場合、それは別の問題です-しかし、単純な方法でpop()はそこでは役に立ちません。
jhfrontz 2016年

なぜ残念ですか?エレガントでシンプルです。
OS2

1

私はこれを行います(C ++ 14を使用):

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

この方法は、エイリアス/ typedefを構築したくない重要なキュータイプがある場合に役立ちます。私は常にこの使用法についてコメントを残すことを忘れないでください、しかし、これは狂っていない、そして実際のclear()方法の代わりに行われたと無防備な/メンテナンスプログラマーに説明します。


なぜ代入演算子で型を明示的に述べるのですか?私はそれmyqueue = { };がうまくいくと思います。
Joel Bodenmann、

0

使い方は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

あなたはそれを削除してキューを空にすることを選択した場合、それはOKですが、それは問題が何であるかではない、とunique_ptrをが入って来なぜ私は表示されません。
manuell

0

別のオプションは、単純なハックを使用して、基になるコンテナーを取得して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';
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.