Do implementacji klasy JobQueue używam std :: queue. (Zasadniczo ta klasa przetwarza każde zadanie w sposób FIFO). W jednym scenariuszu chcę wyczyścić kolejkę za jednym zamachem (usunąć wszystkie zadania z kolejki). Nie widzę żadnej przejrzystej metody dostępnej w klasie std :: queue.
Jak efektywnie wdrożyć przejrzystą metodę dla klasy JobQueue?
Mam jedno proste rozwiązanie polegające na poppingu w pętli, ale szukam lepszych sposobów.
//Clears the job queue
void JobQueue ::clearJobs()
{
// I want to avoid pop in a loop
while (!m_Queue.empty())
{
m_Queue.pop();
}
}
deque
obsługuje jasneOdpowiedzi:
Popularnym idiomem do czyszczenia standardowych kontenerów jest zamiana na pustą wersję kontenera:
Jest to również jedyny sposób na faktyczne wyczyszczenie pamięci przechowywanej w niektórych kontenerach (std :: vector)
źródło
std::queue<int>().swap(q)
. W przypadku idiomu kopiowania i zamiany wszystko to powinno być równoważne zq = std::queue<int>()
.std::queue<int>().swap(q)
jest odpowiednikiem powyższego kodu,q = std::queue<int>()
nie musi być równoważne. Ponieważ nie ma przeniesienia własności w przypisaniu przydzielonej pamięci, niektóre kontenery (takie jak wektor) mogą po prostu wywołać destruktory poprzednio trzymanych elementów i ustawić rozmiar (lub równoważną operację z przechowywanymi wskaźnikami) bez faktycznego zwalniania pamięci.queue
nie maswap(other)
metody, więcqueue<int>().swap(q)
się nie kompiluje. Myślę, że musisz użyć generycznegoswap(a, b)
.Tak - trochę niedopasowanie klasy kolejki, IMHO. Tym się właśnie zajmuję:
źródło
swap
jest „bardziej skuteczny”q1.swap(queue<int>());
q1=queue<int>();
jest krótszy i wyraźniejszy (tak naprawdę nie próbujesz.swap
, próbujesz.clear
).q1 = {}
wystarczyqueue<T>
konstruktor dopasowuje pustą listę argumentów{}
i jest niejawny, więc jest wywoływany, a następnieq1.operator=(queue<T>&&)
konsumuje nowo utworzonąqueue
Autor tematu zapytał, jak „sprawnie” wyczyścić kolejkę, więc zakładam, że chce większej złożoności niż liniowa O (wielkość kolejki) . Formy podawane przez David Rodriguez , Anon mają taką samą złożoność: zgodnie z odpowiednim STL
operator =
ma złożoność O (kolejka wielkości) . IMHO to dlatego, że każdy element kolejki jest zarezerwowany osobno i nie jest przydzielany w jednym dużym bloku pamięci, jak w wektorze. Aby wyczyścić całą pamięć, musimy osobno usunąć każdy element. Zatem najprostszym sposobem na wyczyszczeniestd::queue
jest jedna linia:źródło
O(n^2)
algorytm nadO(n)
algorytmem jeśli stałe na temat funkcjonowania liniowego zrobić to wolniej niż kwadratowego dla wszystkichn < 2^64
, chyba że miałem pewne mocne podstawy, aby sądzić, musiałem szukać przestrzeni adresowej IPv6 lub jakiś inny problem konkretnego. W rzeczywistości wydajność jest dla mnie ważniejsza niż wydajność na granicy.Najwyraźniej istnieją dwa najbardziej oczywiste sposoby na wyczyszczenie
std::queue
: zamiana z pustym obiektem i przypisanie do pustego obiektu.Sugerowałbym użycie przypisania, ponieważ jest po prostu szybsze, bardziej czytelne i jednoznaczne.
Zmierzyłem wydajność za pomocą prostego kodu i odkryłem, że zamiana w wersji C ++ 03 działa o 70-80% wolniej niż przypisanie do pustego obiektu. Jednak w C ++ 11 nie ma różnicy w wydajności. W każdym razie pójdę z zadaniem.
źródło
W C ++ 11 możesz wyczyścić kolejkę, wykonując następujące czynności:
źródło
Możesz utworzyć klasę, która dziedziczy z kolejki i bezpośrednio wyczyścić podstawowy kontener. To jest bardzo wydajne.
Być może Twoja implementacja pozwala również
JobQueue
dziedziczyć obiekt Queue (tutaj )std::queue<Job>
zamiast mieć kolejkę jako zmienną składową. W ten sposób będziesz miał bezpośredni dostęp doc.clear()
swoich funkcji członkowskich.źródło
Zakładając, że
m_Queue
zawiera liczby całkowite:W przeciwnym razie, jeśli zawiera np. Wskaźniki do
Job
obiektów, to:W ten sposób zamieniasz pustą kolejkę na swoją
m_Queue
im_Queue
staje się ona pusta.źródło
Wolałbym nie polegać
swap()
ani ustawiać kolejki na nowo utworzony obiekt kolejki, ponieważ elementy kolejki nie są odpowiednio niszczone. Wywołaniepop()
wywołuje destruktor dla odpowiedniego obiektu elementu. Może to nie być problemem w<int>
kolejkach, ale bardzo dobrze może mieć skutki uboczne w kolejkach zawierających obiekty.Dlatego pętla z
while(!queue.empty()) queue.pop();
wydaje się niestety najbardziej wydajnym rozwiązaniem, przynajmniej dla kolejek zawierających obiekty, jeśli chcesz zapobiec możliwym efektom ubocznym.źródło
swap()
lub przypisanie wywołuje destruktor w nieistniejącej już kolejce, która wywołuje destruktory wszystkich obiektów w kolejce. Teraz, jeśli w Twojej kolejce znajdują się obiekty, które w rzeczywistości są wskaźnikami, jest to inna kwestia - ale prostota teżpop()
Ci w tym nie pomoże.Robię to (używając C ++ 14):
Ten sposób jest przydatny, jeśli masz nietrywialny typ kolejki, dla którego nie chcesz budować aliasu / typedef. Zawsze jednak upewniam się, że zostawiam komentarz na temat tego zastosowania, aby wyjaśnić niczego nie podejrzewającym / konserwującym programistom, że to nie jest szalone i zrobione zamiast rzeczywistej
clear()
metody.źródło
myqueue = { };
to zadziała dobrze.Używanie a
unique_ptr
może być OK.Następnie resetujesz ją, aby uzyskać pustą kolejkę i zwolnić pamięć pierwszej kolejki. Co do złożoności? Nie jestem pewien - ale domyślam się, że to O (1).
Możliwy kod:
źródło
Inną opcją jest skorzystanie z prostego hackowania, aby zdobyć podstawowy kontener
std::queue::c
i wywołaćclear
go. Ten członek musi być obecnystd::queue
zgodnie ze standardem, ale niestety jestprotected
. Hack tutaj pochodzi z tej odpowiedzi .źródło