2009-04-04 14 views
12

यह आप में से कुछ के लिए निराशाजनक प्रतीत हो सकता है, लेकिन एसटीएल कंटेनर पर पुनरावृत्ति के निम्नलिखित 2 तरीकों में से कौन सा बेहतर है? क्यों?सी ++ एसटीएल: एसटीएल कंटेनर पर पुनरावृत्ति की कौन सी विधि बेहतर है?

class Elem; 
typedef vector<Elem> ElemVec; 
ElemVec elemVec; 

// Method 0 
for (ElemVec::iterator i = elemVec.begin(); i != elemVec.end(); ++i) 
{ 
    Elem& e = *i; 
    // Do something 
} 

// Method 1 
for (int i = 0; i < elemVec.size(); ++i) 
{ 
    Elem& e = elemVec.at(i); 
    // Do something 
} 

विधि 0 क्लीनर एसटीएल की तरह लगता है, लेकिन विधि 1 कम कोड के साथ प्राप्त करता है। एक कंटेनर पर सरल पुनरावृत्ति किसी भी स्रोत कोड में सभी दिखाई देता है। इसलिए, मैं विधि 1 चुनने के इच्छुक हूं जो दृश्य अव्यवस्था और कोड आकार को कम करने लगता है।

पीएस: मुझे पता है कि इटरेटर एक साधारण सूचकांक से कहीं अधिक कर सकते हैं। लेकिन, कृपया ऊपर दिखाए गए कंटेनर पर सरल पुनरावृत्ति पर केंद्रित उत्तर/चर्चा रखें।

उत्तर

16

पहला संस्करण किसी भी कंटेनर के साथ काम करता है और इसलिए टेम्पलेट फ़ंक्शंस में अधिक उपयोगी होता है जो किसी भी कंटेनर को पैरामीटर लेता है। यह भी वैक्टरों के लिए भी थोड़ा अधिक कुशल है।

दूसरा संस्करण केवल वैक्टर और अन्य पूर्णांक-अनुक्रमित कंटेनरों के लिए काम करता है। यह उन कंटेनरों के लिए कुछ हद तक अधिक मूर्खतापूर्ण होगा, आसानी से नए आने वालों द्वारा सी ++ में समझा जाएगा, और अगर आपको इंडेक्स के साथ कुछ और करने की ज़रूरत है, तो यह उपयोगी है, जो असामान्य नहीं है।

सामान्य रूप से, कोई आसान नहीं है "यह बेहतर है" उत्तर, मुझे डर है।

+0

धन्यवाद नील। मेरा कोड आमतौर पर टेम्पलेट्स के साथ काम नहीं कर रहा है, लेकिन वेक्टर जिनके प्रकार ज्ञात हैं। क्या आप विस्तार कर सकते हैं कि विधि 0 आपके उत्तर में अधिक कुशल क्यों है? –

+1

अगर इटरेटर वास्तव में सीधे सूचक के रूप में लागू किया जाता है तो यह थोड़ा अधिक कुशल हो सकता है। नोट "मई" और "थोड़ा" शब्दों का उपयोग - आपको सुनिश्चित करने के लिए मापने की आवश्यकता है। –

+0

हां, वास्तव में इटेटरेटर अधिकांश कंटेनरों के लिए सूचक से अधिक कुछ नहीं है। लेकिन, यह कोड को तेज़ी से कैसे बनाता है? AFAIK भी विधि 1 पॉइंटर अंकगणित होने के समाप्त होता है, है ना? –

4

विधि 0 में से कुछ अधिक फायदे:

  • अगर आप वेक्टर से दूसरे कंटेनर में ले जाने के पाश एक ही रहता है,
  • पुनरावर्तक से const_iterator को स्थानांतरित करने के लिए अगर आप की जरूरत है आसान,
  • जब C++ 0x उत्पन्न होगा, ऑटो टाइपिंग कुछ कोड अव्यवस्था को कम करेगा।

मुख्य नुकसान यह है कि कई मामलों में आप दो कंटेनरों को स्कैन करते हैं, इस मामले में एक सूचकांक दो इटरेटर रखने से क्लीनर है।

+0

धन्यवाद डेविड है। मुझे लगता है कि आपका मतलब था विधि 0 ;-) –

+0

हां डेविड, तो क्या आप इसे प्रतिबिंबित करने के लिए अपना उत्तर संपादित कर सकते हैं? धन्यवाद :-) –

+0

हो गया। अपनी पोस्ट को संपादित करने के लिए खेद है, लेकिन यह मुझे भ्रमित कर दिया। ;) – jalf

8

आप एक (बहुत?) दक्षता के छोटे नुकसान कोई आपत्ति नहीं है, मैं का उपयोग कर Boost.Foreach

BOOST_FOREACH(Elem& e, elemVec) 
{ 
    // Your code 
} 
+0

मैं अभी ज्यादातर एक बूस्ट अशिक्षित हूँ। इस टिप Benoît के लिए धन्यवाद! यह एक रखरखाव है :-) –

+0

+1, बेनोइट, मेरे पास जगह पर लूप है और BOOST_FOREACH मुझे सच्चे foreach समर्थन – philsquared

+0

सी ++ के साथ अन्य भाषाओं का उपयोग करने के बाद मुझे सचेत रखता है: std :: for_each। वाक्यविन्यास सिर्फ थोड़ा अलग है;) – jalf

6

विधि 0 तेजी से और इसलिए की सिफारिश की है की सलाह देते हैं।

विधि 1 कंटेनर और एसएलएल कार्यान्वयन के आधार पर आकार() को ओ (1) होने की अनुमति देता है।

+0

धन्यवाद स्टीफन, मैं आकार() :-) –

+0

के बारे में भूल गया था 'हे (एन)'? –

+1

आकार() मानक अनुपालन वेक्टर में ओ (1) होना चाहिए। और यह v.end() से कम कुशल नहीं है क्योंकि यह शायद रेखांकित किया जाएगा। यहां क्षमता केवल वही है (प्रति पहुंच के कुछ निर्देशों से अधिक नहीं) –

1

यह किस प्रकार के कंटेनर पर निर्भर करता है। vector के लिए, इससे कोई फर्क नहीं पड़ता कि आप किस चीज का उपयोग करते हैं। विधि 0 अधिक मूर्खतापूर्ण हो गया है, लेकिन उनका कोई बड़ा अंतर नहीं है, जैसा कि हर कोई कहता है।

आप का उपयोग करने के लिए एक list, बजाय, विधि 1 होता है, सिद्धांत रूप में, O(N) हो फैसला किया, लेकिन वास्तव में कोई सूची at() विधि नहीं है, तो आप भी इसे उस तरह से नहीं कर सकते हैं। (इसलिए कुछ स्तर पर आपका प्रश्न डेक ढेर करता है।)

लेकिन यह स्वयं विधि 0 के लिए एक और तर्क है: यह विभिन्न कंटेनरों के लिए समान वाक्यविन्यास का उपयोग करता है।

2

संयोग से मैंने हाल ही में एक स्पीड टेस्ट बनाया (रैंड() के साथ 10 * 1024 * 1024 इन्स भरना)। जोड़ा STL-एल्गोरिथ्म std :: उत्पन्न करते हैं जो सबसे तेजी से चलाने के लिए लगता है, विशेष इटरेटर-के अनुकूलन (वीसी ++ 2008) की वजह से:
ये 3 रन, नैनो सेकंड

vect[i] time  : 373611869 
vec.at(i) time : 473297793 
*it = time  : 446818590 
arr[i] time  : 390357294 
*ptr time   : 356895778 

अद्यतन में समय कर रहे हैं। माइक्रो सेकंड में समय।

vect[i] time  : 393951 
vec.at(i) time : 551387 
*it = time  : 596080 
generate = time : 346591 
arr[i] time  : 375432 
*ptr time   : 334612 

निष्कर्ष: मानक-एल्गोरिदम का उपयोग करें, वे स्पष्ट लूप से तेज़ हो सकते हैं! (और यह भी अच्छा अभ्यास)

अद्यतन: उपर्युक्त समय आई/ओ-बाउंड स्थिति में थे, मैंने सीपीयू-बाउंड के साथ एक ही परीक्षण किया (अपेक्षाकृत कम वेक्टर पर फिर से शुरू किया गया, जो बार-बार कैश में फिट होना चाहिए, 2 से प्रत्येक तत्व गुणा और वापस वेक्टर को लिखने)

//Visual Studio 2008 Express Edition 
vect[i] time  : 1356811 
vec.at(i) time : 7760148 
*it = time  : 4913112 
for_each = time : 455713 
arr[i] time  : 446280 
*ptr time   : 429595 

//GCC 
vect[i] time  : 431039 
vec.at(i) time : 2421283 
*it = time  : 381400 
for_each = time : 380972 
arr[i] time  : 363563 
*ptr time   : 365971 

दिलचस्प बात यह है iterators और ऑपरेटर [] for_each (जो प्रदर्शन के लिए कुछ टेम्पलेट जादू) के माध्यम से संकेत करने के लिए iterators नीचा करने लगता है की तुलना में कुलपति में काफी धीमी है ++।
जीसीसी एक्सेस समय में केवल() के लिए बदतर है, जो सामान्य है, क्योंकि यह परीक्षणों का एकमात्र रेंज-चेक फ़ंक्शन है।

+0

शायद ही कोई सांख्यिकीय अंतर। 10% के बारे में कुछ भी प्रभाव नहीं पड़ेगा जब एक वास्तविक प्रोग्राम वास्तव में उपयोग में है जब तक कि यह एक अजीब तरह से इस्तेमाल किया गया तंग लूप में न हो। एक कैश मिस, और समय –

+0

के बराबर होगा यदि आप परिभाषित करते हैं _SECURE_SCL 0 # परिभाषित करें _SECURE_SCL_THROWS 0 पॉइंटर और इटरेटर प्रदर्शन के बीच कोई अंतर नहीं होगा। –

2

विधि 0, कई कारणों से।

  • यह बेहतर आपके इरादे, जो संकलक अनुकूलन एड्स के साथ-साथ पठनीयता
  • व्यक्त करता है बंद-एक करके त्रुटियों की संभावना कम
  • यह भी काम करता है अगर आप एक सूची है, जो नहीं करता है 'के साथ वेक्टर की जगह हैं टी ऑपरेटर है []

बेशक, सबसे अच्छा समाधान अक्सर समाधान 2 होगा: std एल्गोरिदम में से एक। std :: for_each, std :: transform, std :: copy या जो कुछ भी आपको चाहिए। यह कुछ और लाभ हैं:

  • यह और भी बेहतर अपने इरादे को व्यक्त करता है, और अनुमति देता है कुछ महत्वपूर्ण संकलक अनुकूलन (एमएस के सुरक्षित SCL अपने तरीके 0 और 1 की जाँच कर सीमा करता है, लेकिन एसटीडी एल्गोरिदम पर यह छोड़ देगा)
  • यह कम कोड है (कॉल साइट पर, कम से कम। बेशक आपको लूप बॉडी को प्रतिस्थापित करने के लिए एक मज़ेदार या कुछ लिखना है, लेकिन उपयोग साइट पर, कोड काफी साफ हो जाता है, शायद यह सबसे अधिक मायने रखता है

सामान्य रूप से, अपने कोड को ओवरस्फीसिंग करने से बचें। बिल्कुल निर्दिष्ट करें कि आप क्या करना चाहते हैं, और कुछ भी नहीं। Std al gorithms आमतौर पर वहाँ जाने का रास्ता है, लेकिन उनके बिना भी, अगर आपको सूचकांक i की आवश्यकता नहीं है, तो यह क्यों है? उस मामले में इसके बजाए इटरेटर का प्रयोग करें।

4

मानक पुस्तकालय कंटेनर पर पुनरावृत्ति की निम्न विधि सर्वोत्तम है।

उपयोग (और आगे) के range-based for-loopauto specifier साथ:

// Method 2 
for (auto& e: elemVec) 
{ 
    // Do something with e... 
} 

यह आपके Method 0 के समान है लेकिन कम लिखना, कम रखरखाव की आवश्यकता है और std::begin() और std::end(), सहित के साथ संगत किसी भी कंटेनर के साथ काम करता है सादे-पुराने सरणी।

+1

-1, ऑटो और इस क्यू के लिए सही समतुल्य है, यह कोड भी गलत है, मैं एक इटरेटर – NoSenseEtAl

+1

अच्छा नहीं हूं, क्योंकि मुझे सी ++ 11 पसंद है: +1 – NoSenseEtAl

+0

@NoSenseEtAl: मेरे उत्तर को बेहतर बनाने में मेरी सहायता करने के लिए धन्यवाद ☺ – Johnsyweb

0

एक संभावना से ऊपर नहीं माना जाता: खोजने इस तरह

for (auto i = elemVec.begin(), ii = 0; ii < elemVec.size(); ++i, ++ii) 
{ 
    // Do something with either the iterator i or the index ii 
} 

,: "कुछ करो" के विवरण के आधार पर, एक विधि 0 और विधि 1 एक साथ हो सकता है, तो आप चुन की जरूरत नहीं है सूचकांक या संबंधित सदस्य तक पहुंच दोनों छोटी जटिलता के साथ प्राप्त होते हैं।

संबंधित मुद्दे