मैं std :: कतार को कुशलतापूर्वक कैसे साफ़ करूँ?


166

मैं JobQueue वर्ग को लागू करने के लिए std :: कतार का उपयोग कर रहा हूं। (मूल रूप से यह वर्ग प्रत्येक कार्य FIFO तरीके से करता है)। एक परिदृश्य में, मैं एक शॉट में कतार को साफ़ करना चाहता हूं (कतार से सभी नौकरियों को हटा दें)। मुझे कोई स्पष्ट विधि उपलब्ध नहीं दिखती है :: कतार वर्ग।

मैं JobQueue वर्ग के लिए स्पष्ट पद्धति को कुशलता से कैसे लागू करूं?

मेरे पास लूप में पॉपिंग का एक सरल उपाय है लेकिन मैं बेहतर तरीकों की तलाश कर रहा हूं।

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

3
नोट स्पष्टdeque समर्थन करता है
bobobobo

जवाबों:


257

मानक कंटेनरों को साफ़ करने के लिए एक सामान्य मुहावरा कंटेनर के खाली संस्करण के साथ गमागमन करता है:

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

यह वास्तव में कुछ कंटेनरों के अंदर रखी गई मेमोरी को साफ़ करने का एकमात्र तरीका है (std :: वेक्टर)


41
बेहतर अभी तक है std::queue<int>().swap(q)। मुहावरे की नकल और अदला-बदली के साथ, यह सब बराबर होना चाहिए q = std::queue<int>()
एलेक्जेंड्रे सी।

12
जबकि std::queue<int>().swap(q)ऊपर दिए गए कोड के बराबर है, की q = std::queue<int>()आवश्यकता नहीं के बराबर है। चूंकि आवंटित मेमोरी के असाइनमेंट में स्वामित्व का कोई हस्तांतरण नहीं है, इसलिए कुछ कंटेनर (जैसे वेक्टर) बस पहले से पकड़े गए तत्वों के विनाशकों को कॉल कर सकते हैं और वास्तव में मेमोरी को रिहा किए बिना आकार (या संग्रहीत पॉइंटर्स के साथ समकक्ष संचालन) सेट कर सकते हैं ।
डेविड रॉड्रिग्ज -

6
queueएक swap(other)विधि नहीं है , इसलिए queue<int>().swap(q)संकलन नहीं करता है। मुझे लगता है कि आपको जेनेरिक का उपयोग करना होगा swap(a, b)
डस्टिन बोसवेल

3
@ ThorbjørnLindeijer: C ++ 03 में आप सही हैं, C ++ 11 कतारों में सदस्य फ़ंक्शन के रूप में स्वैप हैं , और इसके अलावा एक निशुल्क फ़ंक्शन अधिभार है जो एक ही प्रकार की दो कतार स्वैप करेगा।
डेविड रॉड्रिग्ज़ - dribeas

10
@ ThorbjørnLindeijer: मूल कतार के उपयोगकर्ताओं के दृष्टिकोण से, उन तत्वों का अस्तित्व नहीं है। आप सही हैं कि वे एक के बाद एक नष्ट हो जाएंगे और लागत रैखिक है, लेकिन वे स्थानीय फ़ंक्शन के अलावा किसी और द्वारा सुलभ नहीं हैं। एक बहुआयामी वातावरण में, आप ताला लगा देंगे, एक गैर-अस्थायी कतार को मूल एक के साथ स्वैप करेंगे, अनलॉक करेंगे (समवर्ती एक्सेस की अनुमति देने के लिए) और स्वैप की गई कतार को मरने देंगे। इस तरह आप महत्वपूर्ण खंड के बाहर विनाश की लागत को आगे बढ़ा सकते हैं।
डेविड रॉड्रिग्ज -

46

हाँ - कतार वर्ग, IMHO की एक मिसफिट सा। मैं यह करता हूं:

#include <queue>
using namespace std;;

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

8
@ न्स्जता कृपया विस्तृत करें swapकि "अधिक प्रभावी" कैसे है
बॉबोबोब

@ बोबोबोबो:q1.swap(queue<int>());
नस्ज़्टा

12
q1=queue<int>();दोनों छोटे और स्पष्ट हैं (आप वास्तव में कोशिश नहीं .swapकर रहे हैं, आप कोशिश कर रहे हैं .clear)।
बोब्बोबो

28
नए C ++ के साथ, बस q1 = {}पर्याप्त है
Mykola Bogdiuk

2
पर @Ari वाक्य रचना (2) list_initialization और (10) पर operator_assignment । डिफ़ॉल्ट queue<T>निर्माता खाली तर्क सूची से मेल खाता है {}और अंतर्निहित है, इसलिए इसे कहा जाता है, फिर q1.operator=(queue<T>&&)नए बनाए गए उपभोग करता हैqueue
Mykola Bogdiuk

26

विषय के लेखक ने पूछा कि कतार को "कुशलतापूर्वक" कैसे साफ़ किया जाए, इसलिए मुझे लगता है कि वह रैखिक ओ (कतार आकार) की तुलना में बेहतर जटिलता चाहता है । डेविड रोड्रिगेज द्वारा दिए गए तरीके , एनॉन में एक ही जटिलता है: एसटीएल संदर्भ के अनुसार, operator =जटिलता ओ (कतार आकार) है । IMHO यह इसलिए है क्योंकि कतार का प्रत्येक तत्व अलग-अलग आरक्षित है और यह वेक्टर की तरह एक बड़े मेमोरी ब्लॉक में आवंटित नहीं किया गया है। इसलिए सभी मेमोरी को साफ़ करने के लिए, हमें हर एलिमेंट को अलग से डिलीट करना होगा। तो स्पष्ट करने का सबसे सीधा तरीका std::queueएक पंक्ति है:

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

5
यदि आप वास्तविक डेटा पर काम कर रहे हैं तो आप ऑपरेशन की ओ जटिलता को नहीं देख सकते। मैं एक O(n^2)एल्गोरिथ्म पर एक O(n)एल्गोरिथ्म ले जाऊंगा यदि रैखिक ऑपरेशन पर स्थिरांक इसे सभी के लिए द्विघात से धीमा बनाते हैं n < 2^64, जब तक कि मुझे यह विश्वास करने के लिए कुछ मजबूत कारण नहीं था कि मुझे IPv6 पता स्थान या कुछ अन्य विशिष्ट समस्या की खोज करनी थी। वास्तविकता में प्रदर्शन मेरे लिए सीमा से अधिक महत्वपूर्ण है।
डेविड स्टोन

2
स्वीकार किए गए उत्तर की तुलना में यह बेहतर जवाब है क्योंकि आंतरिक रूप से कतार नष्ट होने पर भी ऐसा करती है। तो स्वीकृत उत्तर O (n) है और यह ब्रांड की नई कतार के लिए अतिरिक्त आवंटन और आरंभीकरण करता है।
शीतल शाह

याद रखें, O (n) का मतलब n जटिलता से कम या बराबर है। तो, हाँ, कुछ मामलों में जैसे कतार <वेक्टर <int >>, इसे प्रत्येक तत्व 1 को 1 से नष्ट करने की आवश्यकता होगी, जो किसी भी तरह से धीमा होगा, लेकिन कतार <int> में, स्मृति वास्तव में एक बड़े ब्लॉक में आवंटित की जाती है, और इसलिए इसे आंतरिक तत्वों को नष्ट करने की आवश्यकता नहीं है, और इसलिए कतार के विध्वंसक एकल कुशल नि: शुल्क () ऑपरेशन का उपयोग कर सकते हैं जो लगभग निश्चित रूप से ओ (एन) समय से कम लेता है।
बेंजामिन

15

स्पष्ट रूप से, स्पष्ट करने के दो सबसे स्पष्ट तरीके हैं std::queue: खाली वस्तु के साथ स्वैप करना और खाली वस्तु को असाइन करना।

मैं असाइनमेंट का उपयोग करने का सुझाव दूंगा क्योंकि यह अधिक तेज़, अधिक पठनीय और असंदिग्ध है।

मैंने सरल कोड का उपयोग करके प्रदर्शन को मापा और मैंने पाया कि सी ++ 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();
    }
};

हो सकता है कि आपका कार्यान्वयन आपके कतार ऑब्जेक्ट (यहां JobQueue) को std::queue<Job>एक सदस्य चर के रूप में कतार होने के बजाय विरासत में प्राप्त करने की अनुमति देता है । इस तरह c.clear()आपके सदस्य कार्यों में आपकी सीधी पहुँच होगी ।


9
एसटीएल कंटेनरों को इनहेरिट करने के लिए डिज़ाइन नहीं किया गया है। इस मामले में आप शायद ठीक हैं क्योंकि आप किसी भी अतिरिक्त सदस्य चर को नहीं जोड़ रहे हैं, लेकिन यह सामान्य रूप से अच्छी बात नहीं है।
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();यदि आप संभावित दुष्प्रभावों को रोकना चाहते हैं, तो कम से कम कतारों के लिए कम से कम सबसे कारगर उपाय होना दुर्भाग्य से लगता है।


3
swap()या असाइनमेंट डिस्ट्रक्टर को अब डिफंक्शन क्यू पर कहता है, जो सभी ऑब्जेक्ट्स के डिस्ट्रक्टर्स को क्यू पर कॉल करता है। अब, अगर आपकी कतार में ऐसी वस्तुएं हैं जो वास्तव में संकेत हैं, तो यह एक अलग मुद्दा है - लेकिन एक साधारण pop()आपको वहां भी मदद नहीं करेगा।
२२:०२ पर jhfrontz

दुर्भाग्य से क्यों? यह सुरुचिपूर्ण और सरल है।
OS2

1

मैं ऐसा करता हूं (C ++ 14 का उपयोग करके):

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

यह तरीका उपयोगी है यदि आपके पास एक गैर-तुच्छ कतार प्रकार है, जिसके लिए आप कोई अन्य उपनाम / टाइपफ़ेड नहीं बनाना चाहते हैं। मैं हमेशा इस उपयोग के आस-पास एक टिप्पणी छोड़ना सुनिश्चित करता हूं, हालांकि, प्रोग्रामर / रखरखाव प्रोग्रामर को यह समझाने के लिए कि यह पागल नहीं है, और एक वास्तविक clear()विधि के बदले में किया गया है ।


आप असाइनमेंट ऑपरेटर में स्पष्ट रूप से टाइप क्यों करते हैं? मुझे लगता है कि myqueue = { };बस ठीक काम करेगा।
जोएल बोडेनमैन

0

का उपयोग करना unique_ptrठीक हो सकता है।
फिर आप इसे एक खाली कतार प्राप्त करने और पहली कतार की मेमोरी जारी करने के लिए रीसेट करते हैं। जटिलता के रूप में? मुझे यकीन नहीं है - लेकिन लगता है कि यह ओ (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 में आता है।
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';
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.