std :: queue를 효율적으로 지우려면 어떻게해야합니까?


166

JobQueue 클래스를 구현하기 위해 std :: queue를 사용하고 있습니다. 기본적으로이 클래스는 각 작업을 FIFO 방식으로 처리합니다. 한 시나리오에서 한 번에 대기열을 지우고 싶습니다 (대기열에서 모든 작업 삭제). std :: queue 클래스에서 사용할 수있는 명확한 방법이 없습니다.

JobQueue 클래스에 대한 명확한 메소드를 효율적으로 구현하는 방법은 무엇입니까?

루프에서 터지는 간단한 솔루션이 있지만 더 나은 방법을 찾고 있습니다.

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

3

답변:


257

표준 컨테이너를 지우는 일반적인 관용구는 빈 버전의 컨테이너로 바꾸는 것입니다.

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

또한 일부 컨테이너 (std :: vector) 안에있는 메모리를 실제로 지우는 유일한 방법입니다


41
더 나은 아직입니다 std::queue<int>().swap(q). copy 및 swap 관용구를 사용하면이 모든 것이와 동일해야합니다 q = std::queue<int>().
Alexandre C.

12
std::queue<int>().swap(q)위의 코드와 동일 하지만 동일 할 q = std::queue<int>()필요는 없습니다. 할당 된 메모리를 할당 할 때 소유권이 이전되지 않기 때문에 벡터와 같은 일부 컨테이너는 이전에 보유한 요소의 소멸자를 호출하고 실제로 메모리를 해제하지 않고 크기 (또는 저장된 포인터와 동등한 작업)를 설정할 수 있습니다 .
David Rodríguez-dribeas

6
queue이없는 swap(other)방법을, 그래서 queue<int>().swap(q)컴파일되지 않습니다. 나는 당신이 generic을 사용해야한다고 생각합니다 swap(a, b).
더스틴 보스 웰

3
@ ThorbjørnLindeijer : C ++ 03에서는 C ++ 11에서 큐가 멤버 함수로 스왑 을 가지며 추가로 동일한 유형의 두 큐를 스왑하는 무료 함수 오버로드가 있습니다.
David Rodríguez-dribeas

10
@ ThorbjørnLindeijer : 원래 대기열 사용자의 관점에서 볼 때 이러한 요소는 존재하지 않습니다. 그것들은 차례로 파괴되고 비용은 선형 적이지만 로컬 기능 이외의 다른 사람은 액세스 할 수 없다는 것이 정확합니다. 다중 스레드 환경에서는 임시가 아닌 큐를 원래 큐로 바꾸고 잠금을 해제하고 (동시 액세스를 허용하기 위해) 스왑 된 큐를 죽입니다. 이런 식으로 파괴 비용을 중요 구역 밖으로 옮길 수 있습니다.
David Rodríguez-dribeas

46

예-대기열 클래스 IMHO의 약간의 잘못된 기능. 이것이 제가하는 것입니다:

#include <queue>
using namespace std;;

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

8
@Naszta하는 방법을 자세히 설명하십시오 swap"더 효과적"입니다
bobobobo이

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

12
q1=queue<int>();더 짧고 명확합니다 ( 실제로 하지 않으려 .swap고 노력하고 있습니다 .clear).
bobobobo

28
새로운 C로 ++, 단지 q1 = {}충분하다
Mykola Bogdiuk

2
@Ari에서의 구 (2) list_initialization 및시 (10) operator_assignment . 기본 queue<T>생성자는 빈 인수 목록과 일치 {}다음과 호출 될 수 있도록, 암시 적 q1.operator=(queue<T>&&)새로 생성 된 소비queue
Mykola Bogdiuk

26

주제의 저자는 큐를 "효율적으로"지우는 방법을 물었으므로 linear O (queue size) 보다 더 복잡한 것을 원한다고 가정 합니다. 에 의해 제공 방법 데이비드 로드리게스 , 아논가 동일한 복잡성을 가지고 STL 기준에 따른 operator =복잡도 갖는다 O (큐 사이즈) . IMHO 큐의 각 요소는 별도로 예약되어 있으며 벡터와 같이 하나의 큰 메모리 블록에 할당되지 않기 때문입니다. 따라서 모든 메모리를 지우려면 모든 요소를 ​​개별적으로 삭제해야합니다. 따라서 가장 명확한 방법 std::queue은 한 줄입니다.

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

5
실제 데이터로 작업하는 경우 작업의 O 복잡성을 볼 수 없습니다. 나는 걸릴 것 O(n^2)이상 알고리즘을 O(n)선형 작업에 대한 상수는 모든 차보다 느리게 그것을 만들 경우 알고리즘 n < 2^64내가 IPv6 주소 공간 또는 다른 특정 문제를 검색했다 생각하는 몇 가지 강력한 이유가없는 한,. 실제 성능은 한계 성능보다 나에게 더 중요합니다.
David Stone

2
내부 큐가 파괴 될 때 어쨌든이 작업을 수행하기 때문에 허용 된 답변보다 더 나은 답변입니다. 따라서 허용되는 대답은 O (n)이며 새로운 대기열에 대한 추가 할당 및 초기화를 수행합니다.
Shital Shah

O (n)은 n 복잡도 이하임을 의미합니다. 예, queue <vector <int >>와 같은 경우에는 각 요소를 1 씩 1 씩 제거해야합니다. 어느 쪽이든 속도가 느리지 만 queue <int>에서는 실제로 하나의 큰 블록에 메모리가 할당됩니다. 따라서 내부 요소를 파괴 할 필요가 없으므로 대기열의 소멸자는 거의 확실하게 O (n) 시간 미만의 단일 효율적인 free () 작업을 사용할 수 있습니다.
Benjamin

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

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 객체 (here JobQueue)를 상속 할 수도 있습니다 std::queue<Job>. 이렇게하면 c.clear()멤버 함수에서 직접 액세스 할 수 있습니다.


9
STL 컨테이너는 상속되지 않도록 설계되었습니다. 이 경우 멤버 변수를 추가하지 않기 때문에 괜찮을 것입니다. 그러나 일반적으로 수행하는 것은 좋지 않습니다.
bstamour

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();불행히도 부작용을 방지하려면 객체가 포함 된 대기열에 대해 with with loop가 가장 효율적인 솔루션 인 것처럼 보입니다.


3
swap()또는 할당은 현재 기능이없는 큐에서 소멸자를 호출합니다. 큐는 큐에있는 모든 오브젝트의 소멸자를 호출합니다. 큐에 실제로 포인터 인 객체가있는 경우에는 다른 문제입니다. 그러나 간단한 방법으로 pop()는 도움이되지 않습니다.
jhfrontz

불행히도 왜? 우아하고 간단합니다.
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

대기열을 삭제하여 비우기를 선택해도 괜찮습니다. 그러나 그것은 문제가 아니며, unique_ptr이 왜
나오는지 모르겠습니다

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.