मैं C ++ 11 में मानक लाइब्रेरी कंटेनर का कुशलता से चयन कैसे कर सकता हूं?


135

"C ++ कंटेनर पसंद" नामक एक अच्छी तरह से ज्ञात छवि (चीट शीट) है। यह वांछित उपयोग के लिए सबसे अच्छा कंटेनर चुनने के लिए एक फ्लो चार्ट है।

किसी को पता है कि अगर वहाँ पहले से ही एक सी + + 11 संस्करण है?

यह पिछले एक है: eC ++ कंटेनर पसंद


6
इससे पहले ऐसा कभी नहीं देखा गया। धन्यवाद!
वीसलफॉक्स

6
@WeaselFox: यह पहले से ही C ++ का एक हिस्सा है - यहाँ SO पर।
आलोक सेव

4
C ++ 11 ने केवल एक नया सच्चा कंटेनर प्रकार पेश किया: unordered_X कंटेनर। उन्हें शामिल करना केवल तालिका को काफी खराब कर देगा, क्योंकि हैश-तालिका उपयुक्त है या नहीं यह तय करते समय कई विचार हैं।
निकोल बोलस

13
जेम्स सही है, वेक्टर का उपयोग करने के लिए अधिक मामले हैं जो तालिका दिखाती है। डेटा स्थानीयता का लाभ कई मामलों में कुछ कार्यों में दक्षता की कमी (बहुत जल्द C ++ 11)। मुझे c ++ 03 के लिए भी ई चार्ट इतना उपयोगी नहीं लगता
डेविड रॉड्रिग्ज़ -

33
यह प्यारा है, लेकिन मुझे लगता है कि डेटा संरचनाओं पर किसी भी आम पाठ्यपुस्तक को पढ़ने से आपको ऐसी स्थिति में छोड़ दिया जाएगा, जिससे आप न केवल कुछ मिनटों में इस फ़्लोचार्ट को पुनर्जीवित कर सकते हैं, बल्कि यह भी जान सकते हैं कि इस फ़्लोचार्ट पर बहुत अधिक उपयोगी सामग्री है।
एंड्रयू टॉमाज़ोस

जवाबों:


97

ऐसा नहीं है कि मुझे पता है, हालांकि यह मेरे अनुमान से किया जा सकता है। इसके अलावा, चार्ट थोड़ा बंद है, क्योंकि listसामान्य रूप से इतना अच्छा कंटेनर नहीं है, और न ही है forward_list। दोनों सूची आला अनुप्रयोगों के लिए बहुत विशिष्ट कंटेनर हैं।

ऐसा चार्ट बनाने के लिए, आपको बस दो सरल दिशानिर्देशों की आवश्यकता है:

  • पहले शब्दार्थ के लिए चुनें
  • जब कई विकल्प उपलब्ध होते हैं, तो सबसे सरल के लिए जाएं

प्रदर्शन के बारे में चिंता करना आमतौर पर पहली बार में बेकार है। जब आप कुछ हज़ारों (या अधिक) आइटम को हैंडल करना शुरू करते हैं, तो बड़े ओ विचार केवल वास्तव में किक करते हैं।

कंटेनरों की दो बड़ी श्रेणियां हैं:

  • सहयोगी कंटेनर: उनके पास एक findऑपरेशन है
  • सरल अनुक्रम कंटेनर

और फिर आप उनमें से शीर्ष पर कई एडेप्टर का निर्माण कर सकते हैं: stack, queue, priority_queue। मैं यहां से एडाप्टरों को छोड़ दूंगा, वे पहचानने योग्य होने के लिए पर्याप्त रूप से विशिष्ट हैं।


प्रश्न 1: साहचर्य ?

  • यदि आपको आसानी से एक कुंजी द्वारा खोज करने की आवश्यकता है , तो आपको एक सहयोगी कंटेनर की आवश्यकता है
  • यदि आपको तत्वों को क्रमबद्ध करने की आवश्यकता है, तो आपको एक ऑर्डर किए गए सहयोगी कंटेनर की आवश्यकता है
  • अन्यथा, प्रश्न 2 पर जाएं।

प्रश्न 1.1: आदेश दिया गया ?

  • यदि आपको किसी विशिष्ट आदेश की आवश्यकता नहीं है, तो unordered_कंटेनर का उपयोग करें, अन्यथा उसके पारंपरिक आदेश वाले समकक्ष का उपयोग करें।

प्रश्न 1.2: अलग कुंजी ?

  • यदि कुंजी मान से अलग है, तो a का उपयोग करें map, अन्यथा a का उपयोग करेंset

प्रश्न १.३: नकल ?

  • यदि आप डुप्लिकेट रखना चाहते हैं, तो एक का उपयोग करें multi, अन्यथा नहीं।

उदाहरण:

मान लीजिए कि मेरे पास कई लोग हैं, जिनके साथ एक विशिष्ट आईडी जुड़ी हुई है, और मैं अपनी आईडी से एक व्यक्ति के डेटा को यथाशीघ्र पुनः प्राप्त करना चाहूंगा।

  1. मैं एक findफ़ंक्शन चाहता हूं , इस प्रकार एक सहयोगी कंटेनर

    1.1। मैं आदेश के बारे में कम परवाह नहीं कर सकता, इस प्रकार एक unordered_कंटेनर

    1.2। मेरी कुंजी (आईडी) उस मूल्य से अलग है जो इसके साथ जुड़ा हुआ है, इस प्रकार एmap

    1.3। आईडी अद्वितीय है, इस प्रकार किसी डुप्लिकेट को रेंगना नहीं चाहिए।

अंतिम जवाब है: std::unordered_map<ID, PersonData>


प्रश्न 2: मेमोरी स्थिर ?

  • यदि तत्वों को मेमोरी में स्थिर होना चाहिए (अर्थात, कंटेनर के संशोधित होने पर उन्हें घूमना नहीं चाहिए), तो कुछ का उपयोग करें list
  • अन्यथा, प्रश्न 3 पर जाएं।

प्रश्न २.१: कौन सा ?

  • के लिए सेटल list; ए forward_listकेवल कम स्मृति पदचिह्न के लिए उपयोगी है।

प्रश्न 3: डायनामिक रूप से आकार ?

  • यदि कंटेनर में एक ज्ञात आकार (संकलन समय पर) है, और यह आकार कार्यक्रम के दौरान बदल नहीं जाएगा, और तत्व डिफ़ॉल्ट रूप से निर्माण योग्य हैं या आप एक पूर्ण आरंभीकरण सूची ( { ... }सिंटैक्स का उपयोग करके ) प्रदान कर सकते हैं , तो एक का उपयोग करें array। यह पारंपरिक सी-सरणी की जगह लेता है, लेकिन सुविधाजनक कार्यों के साथ।
  • अन्यथा, प्रश्न 4 पर जाएं।

प्रश्न 4: डबल-एंडेड ?

  • यदि आप आगे और पीछे दोनों तरफ से वस्तुओं को हटाने में सक्षम होना चाहते हैं, तो ए का उपयोग करें deque, अन्यथा ए का उपयोग करें vector

आप ध्यान दें कि, जब तक आपको एक सहयोगी कंटेनर की आवश्यकता नहीं होगी, तब तक आपकी पसंद ए होगी vector। यह पता चला है कि यह सटर और स्ट्रॉस्ट्रुप की सिफारिश भी है


5
+1, लेकिन कुछ नोटों के साथ: 1) arrayएक डिफ़ॉल्ट रचनात्मक प्रकार की आवश्यकता नहीं है; 2) उठा multiरों इतना डुप्लिकेट के बारे में नहीं है किया जा रहा अनुमति लेकिन है कि क्या के बारे में अधिक रखने के लिए उन्हें मायने रखती है (आप गैर में डुप्लिकेट डाल सकते हैं multiकंटेनरों, यह सिर्फ होता है कि केवल एक ही रखा जाता है)।
आर। मार्टिनो फर्नांडिस

2
उदाहरण थोड़ा हटकर है। 1) हम "ढूंढ सकते हैं" (सदस्य फ़ंक्शन नहीं, "<एल्गोरिथम>" एक) एक गैर साहचर्य कंटेनर पर, 1.1) अगर हमें "कुशलतापूर्वक" खोजने की आवश्यकता है, और unordered_ O (1) होगा और O (नहीं) लॉग एन)।
ब्लाकबैट

4
@BlakBat: हालांकि, की map.find(key)तुलना में बहुत अधिक स्वादिष्ट है std::find(map.begin(), map.end(), [&](decltype(map.front()) p) { return p.first < key; }));, इसलिए यह महत्वपूर्ण है, शब्दार्थ से, यह findएक से एक के बजाय एक सदस्य समारोह है <algorithm>। ओ (1) बनाम ओ (लॉग एन) के लिए, यह शब्दार्थ को प्रभावित नहीं करता है; मैं उदाहरण से "कुशलतापूर्वक" निकालूंगा और इसे "आसानी से" से बदल दूंगा।
मैथ्यू एम।

"यदि तत्वों को स्मृति में स्थिर होना चाहिए ... तो कुछ सूची का उपयोग करें" ... हम्म, मैंने सोचा dequeकि यह संपत्ति भी थी?
मार्टिन बा

@MartinBa: हाँ और नहीं। एक में dequeतत्वों स्थिर रहे हैं केवल यदि आप धक्का दोनों छोर पर / पॉप; यदि आप बीच में सम्मिलित करना / मिटाना शुरू करते हैं तो बनाए गए अंतर को भरने के लिए N / 2 तत्वों तक को बदल दिया जाता है।
मैथ्यू एम।

51

मुझे मैथ्यू का जवाब पसंद है, लेकिन मैं फ्लोचार्ट को इस तरह से संशोधित करने जा रहा हूं:

जब एसटीडी का उपयोग न करें :: वेक्टर

डिफ़ॉल्ट रूप से, यदि आपको किसी सामान की आवश्यकता है, तो उपयोग करें std::vector। इस प्रकार, हर दूसरे कंटेनर को केवल कुछ कार्यक्षमता विकल्प प्रदान करके उचित ठहराया जाता है std::vector

कंस्ट्रक्टर्स

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

A std::dequeसामान्य प्रतिस्थापन होगा, जिसके कई गुण होंगे std::vector, लेकिन आप केवल छल के दोनों सिरों पर सम्मिलित कर सकते हैं। बीच में आवेषण को हिलाने की आवश्यकता होती है। एक std::listउसकी सामग्री पर कोई आवश्यकता देता है।

बोल्स की जरूरत है

std::vector<bool>नहीं है। खैर, यह मानक है। लेकिन यह vectorसामान्य अर्थों में नहीं है, क्योंकि std::vectorआम तौर पर अनुमति देने वाले संचालन निषिद्ध हैं। और यह सबसे निश्चित रूप से शामिल नहीं है boolरों

इसलिए, यदि आपको एस के vectorकंटेनर से वास्तविक व्यवहार की आवश्यकता है, तो आप boolइसे प्राप्त नहीं करेंगे std::vector<bool>। तो आप एक के साथ बनाने के लिए होगा std::deque<bool>

खोज कर

यदि आपको एक कंटेनर में तत्वों को खोजने की आवश्यकता है, और खोज टैग केवल एक सूचकांक नहीं हो सकता है, तो आपको और के std::vectorपक्ष में त्यागने की आवश्यकता हो सकती है । कुंजी शब्द " हो सकता है " पर ध्यान दें ; एक क्रमबद्ध कभी-कभी एक उचित विकल्प होता है। या Boost.Container , जो एक छँटाई को लागू करता है ।setmapstd::vectorflat_set/mapstd::vector

अब इनमें से चार विविधताएं हैं, जिनमें से प्रत्येक की अपनी आवश्यकताएं हैं।

  • mapजब खोज टैग का उपयोग उसी चीज़ के रूप में न हो, जिस आइटम को आप स्वयं ढूंढ रहे हैं। अन्यथा एक का उपयोग करें set
  • उपयोग करें unorderedजब आपके पास कंटेनर में बहुत सी वस्तुएं हैं और खोज प्रदर्शन बिल्कुल आवश्यक है O(1), बजाय O(logn)
  • multiयदि आपको एक ही खोज टैग के लिए कई वस्तुओं की आवश्यकता हो तो उपयोग करें ।

आदेश

यदि आपको किसी विशेष तुलना ऑपरेशन के आधार पर हमेशा वस्तुओं के एक कंटेनर की आवश्यकता होती है, तो आप एक का उपयोग कर सकते हैं set। या multi_setयदि आपको एक ही मूल्य के लिए कई वस्तुओं की आवश्यकता है।

या आप किसी सॉर्ट का उपयोग कर सकते हैं std::vector, लेकिन आपको इसे क्रमबद्ध रखना होगा।

स्थिरता

जब पुनरावृत्तियों और संदर्भों को अमान्य कर दिया जाता है तो कभी-कभी चिंता होती है। यदि आपको वस्तुओं की एक सूची की आवश्यकता है, जैसे कि आपके पास विभिन्न मदों में उन वस्तुओं के लिए पुनरावृत्तियाँ / संकेत हैं, तो std::vectorअमान्य होने का दृष्टिकोण उचित नहीं हो सकता है। कोई भी प्रविष्टि ऑपरेशन वर्तमान आकार और क्षमता के आधार पर अमान्य हो सकता है।

std::listएक फर्म गारंटी प्रदान करता है: एक इट्रेटर और उसके संबंधित संदर्भ / पॉइंटर्स केवल तभी अमान्य होते हैं जब आइटम को कंटेनर से निकाल दिया जाता है। std::forward_listवहाँ है अगर स्मृति एक गंभीर चिंता का विषय है।

यदि यह बहुत मजबूत है, तो std::dequeएक कमजोर लेकिन उपयोगी गारंटी प्रदान करता है। बीच में सम्मिलन से अमान्य परिणाम प्राप्त होते हैं, लेकिन सिर या पूंछ पर सम्मिलन केवल पुनरावृत्तियों के अमान्यकरण का कारण बनता है , न कि कंटेनर में आइटम के संकेत / संदर्भ।

प्रविष्टि प्रदर्शन

std::vector केवल अंत में सस्ते प्रविष्टि प्रदान करता है (और फिर भी, यह महंगा हो जाता है यदि आप क्षमता उड़ाते हैं)।

std::listप्रदर्शन के मामले में महंगा है (प्रत्येक नए सम्मिलित आइटम में एक मेमोरी आवंटन लागत है), लेकिन यह सुसंगत है । यह वस्तुतः बिना किसी प्रदर्शन लागत के साथ-साथ वस्तुओं के फेरबदल की अपरिहार्य क्षमता भी प्रदान करता है, साथ ही साथ std::listप्रदर्शन के किसी भी नुकसान में उसी प्रकार के अन्य कंटेनरों के साथ वस्तुओं का व्यापार करता है। अगर आपको बहुत सी चीजों को इधर-उधर करने की जरूरत है , तो उपयोग करें std::list

std::dequeसिर और पूंछ पर निरंतर-समय सम्मिलन / निष्कासन प्रदान करता है, लेकिन बीच में सम्मिलन काफी महंगा हो सकता है। इसलिए यदि आपको सामने से और पीछे से चीजों को जोड़ने / हटाने की जरूरत है, तो std::dequeहो सकता है कि आपको क्या चाहिए।

यह ध्यान दिया जाना चाहिए कि, शब्दार्थ को स्थानांतरित करने के लिए धन्यवाद, std::vectorसम्मिलन का प्रदर्शन उतना बुरा नहीं हो सकता है जितना पहले हुआ करता था। कुछ कार्यान्वयनों ने मूवमेंट-आधारित आइटम कॉपी (तथाकथित "स्वैप्टिमाइज़ेशन") के एक रूप को लागू किया, लेकिन अब यह चलती भाषा का हिस्सा है, यह मानक द्वारा अनिवार्य है।

कोई गतिशील आवंटन नहीं

std::arrayयदि आप सबसे कम संभव गतिशील आवंटन चाहते हैं तो एक अच्छा कंटेनर है। यह सी-एरे के चारों ओर केवल एक आवरण है; इसका अर्थ है कि इसका आकार संकलन-समय पर ज्ञात होना चाहिए । यदि आप उस के साथ रह सकते हैं, तो उपयोग करें std::array

कहा जा रहा है, एक आकार का उपयोग std::vectorऔर reserveआईएनजी बस के रूप में अच्छी तरह से एक बंधे के लिए काम करेगा std::vector। इस तरह, वास्तविक आकार अलग-अलग हो सकता है, और आपको केवल एक मेमोरी आवंटन मिलता है (जब तक कि आप क्षमता को नहीं उड़ाते)।


1
खैर, एक बहुत भी :) WRT एक वेक्टर रखने मैं अपने जवाब की तरह हल कर, अलग से std::sort, वहाँ भी है std::inplace_mergeजो आसानी से नए तत्व (बजाय एक से जगह दिलचस्प है std::lower_bound+ std::vector::insertकॉल)। flat_setऔर के बारे में जानकर अच्छा लगा flat_map!
मैथ्यू एम।

2
आप 16-बाइट संरेखित प्रकारों के साथ एक वेक्टर का उपयोग नहीं कर सकते। इसके अलावा एक अच्छा प्रतिस्थापन भी vector<bool>है vector<char>
उलटा

@ उल्टा: "आप 16-बाइट संरेखित प्रकारों के साथ एक वेक्टर का उपयोग नहीं कर सकते हैं।" कौन कहता है? यदि std::allocator<T>वह संरेखण का समर्थन नहीं करता है (और मुझे नहीं पता कि यह क्यों नहीं होगा), तो आप हमेशा अपने स्वयं के कस्टम आवंटनकर्ता का उपयोग कर सकते हैं।
निकोल बोलस

2
@ इनवर्स: C ++ 11 का std::vector::resizeएक अधिभार है जो एक मान नहीं लेता है (यह सिर्फ नए आकार लेता है; कोई भी नए तत्व डिफ़ॉल्ट रूप से इन-प्लेस होंगे)। इसके अलावा, कंपाइलर वैल्यू पैरामीटर को ठीक से अलाइन करने में असमर्थ क्यों होते हैं, तब भी जब वे उस एलाइनमेंट के लिए घोषित किए जाते हैं।
निकोल बोलस

1
bitsetबूल के लिए यदि आप अग्रिम में आकार जानते हैं। en.cppreference.com/w/cpp/utility/bitset
bendervader

25

यहाँ उपरोक्त फ्लोचार्ट का C ++ 11 संस्करण है। [मूल लेखक के बिना मूल रूप से पोस्ट किए गए, मिकेल पर्ससन ]


2
@NO_NAME वाह, मुझे खुशी है कि किसी ने स्रोत का हवाला देते हुए परेशान किया।
अंडरस्कोर_ड

1

यहां एक त्वरित स्पिन है, हालांकि इसे शायद काम की जरूरत है

Should the container let you manage the order of the elements?
Yes:
  Will the container contain always exactly the same number of elements? 
  Yes:
    Does the container need a fast move operator?
    Yes: std::vector
    No: std::array
  No:
    Do you absolutely need stable iterators? (be certain!)
    Yes: boost::stable_vector (as a last case fallback, std::list)
    No: 
      Do inserts happen only at the ends?
      Yes: std::deque
      No: std::vector
No: 
  Are keys associated with Values?
  Yes:
    Do the keys need to be sorted?
    Yes: 
      Are there more than one value per key?
      Yes: boost::flat_map (as a last case fallback, std::map)
      No: boost::flat_multimap (as a last case fallback, std::map)
    No:
      Are there more than one value per key?
      Yes: std::unordered_multimap
      No: std::unordered_map
  No:
    Are elements read then removed in a certain order?
    Yes:
      Order is:
      Ordered by element: std::priority_queue
      First in First out: std::queue
      First in Last out: std::stack
      Other: Custom based on std::vector????? 
    No:
      Should the elements be sorted by value?
      Yes: boost::flat_set
      No: std::vector

आप देख सकते हैं कि यह C ++ 03 संस्करण से बेतहाशा भिन्न है , मुख्य रूप से इस तथ्य के कारण कि मुझे वास्तव में लिंक किए गए नोड्स पसंद नहीं हैं। लिंक किए गए नोड कंटेनर आमतौर पर गैर-लिंक किए गए कंटेनर द्वारा प्रदर्शन में हराया जा सकता है, कुछ दुर्लभ स्थितियों को छोड़कर। यदि आपको नहीं पता कि वे परिस्थितियां क्या हैं, और बढ़ावा देने के लिए उपयोग किया है, तो लिंक किए गए नोड कंटेनरों का उपयोग न करें। (std :: list, std :: slist, std :: map, std :: multimap, std :: set, std :: multiset)। यह सूची ज्यादातर छोटे और मध्यम पक्षीय कंटेनरों पर केंद्रित है, क्योंकि (ए) यह 99.99% है जो हम कोड में व्यवहार करते हैं, और (बी) बड़ी संख्या में तत्वों को अलग-अलग कंटेनरों की नहीं बल्कि कस्टम एल्गोरिदम की आवश्यकता होती है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.