'+' लूप का उपयोग करके C ++ वेक्टर के माध्यम से Iterate करें


140

मैं C ++ भाषा में नया हूं। मैं वैक्टर का उपयोग करना शुरू कर रहा हूं, और मैंने देखा है कि सभी कोड में मैं इसे पुनरावृति के लिए देखता हूं, हालांकि सूचकांकों के माध्यम से एक वेक्टर, forलूप का पहला पैरामीटर हमेशा वेक्टर के आधार पर कुछ होता है। जावा में मैं एक ArrayList के साथ कुछ ऐसा कर सकता हूं:

for(int i=0; i < vector.size(); i++){
   vector[i].doSomething();
}

वहाँ एक कारण है कि मैं इसे सी ++ में नहीं देखता हूं? क्या यह बुरा अभ्यास है?


1
लूप के लिए कोई फ़ंक्शन नहीं है, इसलिए इसमें पैरामीटर (या तर्क नहीं हैं, जो आप पास हैं)। क्या आपका मतलब कुछ ऐसा है std::vector<int>::size_type i = 0;, हालांकि, या शायद std::vector<int>::iterator it = vector.begin();?
क्रिस

वास्तव में, मेरे द्वारा देखे गए सभी उदाहरण उसी तरह लिखे गए हैं।
फ्लिन

4
जावा में, मैं प्रत्येक लूप के लिए पसंद करूंगा या पुनरावृत्तियों का उपयोग करूंगा। C ++ की तरह ही बहुत अधिक, हालांकि थोड़ा अलग वाक्यविन्यास।
जेसी गुड


10
यहां अधिकांश उत्तर गलत तरीके से Q को मान लेते हैं: सबसे अच्छा / सबसे छोटा रास्ता क्या है std::vector? यहाँ पर पूछे जा रहे वास्तविक क्यू है: क्या कोई कारण है कि मैं इस C ++ को नहीं देख रहा हूँ? क्या यह बुरा अभ्यास है? उर्फ मैं हमेशा C ++ में कोड क्यों देखता हूं जो पुनरावृति करते समय पुनरावृत्तियों का उपयोग करता है std::vector?
आलोक सेव

जवाबों:


92

क्या कोई कारण है जो मैं इसे C ++ में नहीं देखता हूं? क्या यह बुरा अभ्यास है?

नहीं, यह एक बुरा अभ्यास नहीं है, लेकिन निम्नलिखित दृष्टिकोण आपके कोड को निश्चित लचीलापन प्रदान करता है

आमतौर पर, पूर्व-सी ++ 11 कंटेनर तत्वों पर पुनरावृत्ति के लिए कोड पुनरावृत्तियों का उपयोग करता है, कुछ इस प्रकार है:

std::vector<int>::iterator it = vector.begin();

ऐसा इसलिए है क्योंकि यह कोड को अधिक लचीला बनाता है।

सभी मानक पुस्तकालय कंटेनर पुनरावृत्तियों का समर्थन करते हैं और प्रदान करते हैं। यदि विकास के बाद के बिंदु पर आपको किसी अन्य कंटेनर में स्विच करने की आवश्यकता है, तो इस कोड को बदलने की आवश्यकता नहीं है।

नोट: हर संभावित मानक पुस्तकालय कंटेनर के साथ काम करने वाला कोड लिखना उतना आसान नहीं है जितना कि यह प्रतीत हो सकता है।


25
क्या कोई मुझे समझा सकता है कि इस विशेष मामले / कोड स्निपेट में आप अनुक्रमणकों पर पुनरावृत्तियों की सलाह क्यों देते हैं? यह "लचीलापन" क्या आप के बारे में बात कर रहे हैं? निजी तौर पर, मुझे पुनरावृत्तियां पसंद नहीं हैं, वे कोड को ब्लोट करते हैं - समान प्रभाव के लिए टाइप करने के लिए बस अधिक वर्ण। खासकर यदि आप उपयोग नहीं कर सकते auto
वायलेट जिराफ

8
@ वायलेटगिरिफ़: पुनरावृत्तियों का उपयोग करते समय, कुछ मामलों जैसे कि खाली श्रेणियों के साथ गलत हो जाना कठिन होता है और कोड अधिक क्रिया है। अपनी बात या धारणा और पसंद पर विचार करें, इसलिए इस पर अंतहीन बहस की जा सकती है।
आलोक

9
आप केवल इटरेटर की घोषणा कैसे करते हैं लेकिन लूप करने के लिए इसका उपयोग करने का तरीका नहीं दिखाते हैं ...
अंडरस्कोर_ड

116

इस तरह के अभ्यास को न देखने का कारण काफी व्यक्तिपरक है और इसका निश्चित उत्तर नहीं हो सकता है, क्योंकि मैंने कई कोड देखे हैं जो iteratorस्टाइल कोड के बजाय आपके उल्लेखित तरीके का उपयोग करते हैं ।

निम्नलिखित लोगों के कारण हो सकते हैं vector.size()जो लूपिंग के तरीके पर विचार नहीं करते हैं :

  1. size()पाश स्थिति में हर बार कॉल करने के बारे में पागल होना । हालाँकि या तो यह एक गैर-मुद्दा है या इसे तुच्छ रूप से तय किया जा सकता है
  2. पसंद करते हैं std::for_each()अधिक forपाश ही
  3. बाद में कंटेनर std::vectorको दूसरे से बदलना (जैसे map, list) लूपिंग तंत्र को बदलने की भी मांग करेगा, क्योंकि लूपिंग के हर कंटेनर का समर्थन size()शैली नहीं है

सी ++ 11 कंटेनरों के माध्यम से स्थानांतरित करने के लिए एक अच्छी सुविधा प्रदान करता है। इसे "जावा में लूप के लिए आधारित रेंज" (या "लूप के लिए बढ़ाया") कहा जाता है।

छोटे कोड के साथ आप पूर्ण (अनिवार्य!) के माध्यम से पार कर सकते हैं std::vector:

vector<int> vi;
...
for(int i : vi) 
  cout << "i = " << i << endl;

12
बस लूप के लिए आधारित सीमा का एक छोटा नुकसान नोट करने के लिए : आप इसका उपयोग नहीं कर सकते #pragma omp parallel for
18'14 को 3'14

2
मुझे कॉम्पैक्ट संस्करण पसंद है क्योंकि पढ़ने के लिए कम कोड है। एक बार जब आप मानसिक समायोजन कर लेते हैं तो इसे समझना बहुत आसान हो जाता है और बग्स अधिक खड़े हो जाते हैं। यह तब और अधिक स्पष्ट हो जाता है जब एक गैर-मानक पुनरावृत्ति हो रही है क्योंकि कोड का एक बहुत बड़ा हिस्सा है।
कोड एबिनोमेटर

87

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

for (auto it = begin (vector); it != end (vector); ++it) {
    it->doSomething ();
}

या (ऊपर के बराबर)

for (auto & element : vector) {
    element.doSomething ();
}

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

यह शायद आपने देखा है। आपके द्वारा उल्लिखित दृष्टिकोण की तुलना में, लाभ यह है कि आप भारी प्रकार के आधार पर निर्भर नहीं होते हैं vector। यदि आप vectorएक अलग "संग्रह-प्रकार" वर्ग में बदलते हैं , तो आपका कोड शायद काम करेगा। हालाँकि, आप जावा में भी कुछ ऐसा ही कर सकते हैं। वैचारिक रूप से बहुत अंतर नहीं है; C ++, हालांकि, इसे लागू करने के लिए टेम्पलेट्स का उपयोग करता है (जावा में जेनेरिक की तुलना में); इसलिए दृष्टिकोण सभी प्रकार के लिए काम करेगा जिसके लिए beginऔर endफ़ंक्शन परिभाषित किए गए हैं, यहां तक ​​कि गैर-प्रकार के प्रकार जैसे कि स्थिर सरणियाँ। यहां देखें: सादे सरणियों के लिए काम के लिए रेंज-आधारित कैसे होता है?


5
ऑटो, फ्री स्टार्ट / एंड भी C ++ 11 हैं। और भी, आपको कई मामलों में इसके बजाय ++ का उपयोग करना चाहिए।
फॉरएवीआर

हाँ तुम सही हो। कार्यान्वयन beginऔर end, हालांकि, एक-लाइनर है।
जॉनबी

@ जॉन यह एक से अधिक-लाइनर है, क्योंकि यह निश्चित आकार के सरणियों के लिए भी काम करता है। autoदूसरी ओर काफी मुश्किल होगा।
जुआनकोपंजा

यदि आपको वेक्टर के लिए इसकी आवश्यकता है तो यह केवल एक-लाइनर है।
जॉन बी

फिर भी, पहला उदाहरण भ्रामक है, क्योंकि यह C ++ 03 में काम नहीं कर सकता है, जबकि आपका वाक्यांश यह बताता है कि यह करता है।
जुआनकोपंजा

35

ऐसा करने का सही तरीका है:

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    it->doSomething();
 }

जहाँ T वेक्टर के अंदर वर्ग का प्रकार है। उदाहरण के लिए यदि वर्ग CActivity था, तो T के बजाय केवल CActivity लिखें।

इस तरह की विधि हर एसटीएल (न केवल वैक्टर, जो थोड़ा बेहतर है) पर काम करेगी।

यदि आप अभी भी अनुक्रमित का उपयोग करना चाहते हैं, तो तरीका यह है:

for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
    v[i].doSomething();
}

std::vector<T>::size_typeहमेशा नहीं है size_t? यही वह प्रकार है जो मैं हमेशा इसके लिए उपयोग करता हूं।
वायलेट जिराफ

1
@VioletGiraffe मुझे पूरा यकीन है कि आप सही हैं (वास्तव में जाँच नहीं की गई है), लेकिन यह std :: वेक्टर <T> :: size_type का उपयोग करने के लिए बेहतर अभ्यास है।
डिजीएम

8

पुनरावृत्तियों का उपयोग करने के लिए कुछ मजबूत कारण हैं, जिनमें से कुछ का उल्लेख यहां किया गया है:

बाद में कंटेनरों को बदलना आपके कोड को अमान्य नहीं करता है।

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

अमान्य पुनरावृत्ति को पकड़ने वाला रनटाइम

यदि आप अपने कंटेनर को अपने लूप के बीच में संशोधित करते हैं, तो अगली बार जब आप अपने पुनरावृत्त का उपयोग करते हैं तो यह एक अमान्य पुनरावृत्ति अपवाद को फेंक देगा।


1
क्या आप कुछ ऐसे लेख / पोस्ट की ओर संकेत कर सकते हैं जो उपरोक्त बिंदुओं को उदाहरण कोड के साथ बताते हैं? बहुत अच्छा होगा! या यदि आप एक जोड़ सकते हैं :)
अनु

5

मुझे आश्चर्य नहीं हुआ कि किसी ने उल्लेख किया है कि एक पूर्णांक सूचकांक के साथ एक सरणी के माध्यम से पुनरावृत्ति करना आपके लिए गलत सूचकांक के साथ एक सरणी को सबस्क्रिप्ट करके दोषपूर्ण कोड लिखना आसान बनाता है। उदाहरण के लिए, यदि आपके पास नेड्स का उपयोग करने iऔर jसूचकांकों के रूप में नेस्टेड लूप हैं, तो आप गलत तरीके से एक सरणी के jबजाय सबस्क्रिप्ट कर सकते हैंi है और इस तरह इस कार्यक्रम में एक गलती परिचय।

इसके विपरीत, यहां सूचीबद्ध अन्य रूप, अर्थात् श्रेणी आधारित forलूप, और पुनरावृत्त, बहुत कम त्रुटि वाले हैं। भाषा का शब्दार्थ और संकलक का प्रकार जाँच तंत्र आपको गलती से गलत अनुक्रमणिका का उपयोग करके किसी सरणी तक पहुँचने से रोक देगा।


4

एसटीएल के साथ, प्रोग्रामर iteratorsकंटेनरों के माध्यम से ट्रैवर्सिंग के लिए उपयोग करते हैं, क्योंकि इट्रेटर एक सार अवधारणा है, जो सभी मानक कंटेनरों में लागू की जाती है। उदाहरण के लिए, std::listकोई है operator []सब पर।


3

ऑटो ऑपरेटर का उपयोग करना वास्तव में उपयोग करना आसान बनाता है क्योंकि किसी को डेटा प्रकार और वेक्टर के आकार या किसी अन्य ऊर्जा संरचना के बारे में चिंता करने की आवश्यकता नहीं है

वेक्टर का उपयोग ऑटो और लूप के लिए किया जाता है

vector<int> vec = {1,2,3,4,5}

for(auto itr : vec)
    cout << itr << " ";

आउटपुट:

1 2 3 4 5

आप इस विधि का उपयोग सेट और सूची को पुनरावृत्त करने के लिए भी कर सकते हैं। ऑटो का उपयोग करके टेम्पलेट में उपयोग किए गए डेटा प्रकार का स्वचालित रूप से पता लगाता है और आपको इसका उपयोग करने देता है। तो, भले ही हम एक था vectorकी stringया charएक ही वाक्य रचना ठीक काम करेंगे


1

लूप को पुनरावृत्त करने और उसके मूल्यों को प्रिंट करने का सही तरीका इस प्रकार है:

#include<vector>

//declare the vector of type int
vector<int> v;

//insert the 5 element in the vector
for ( unsigned int i = 0; i < 5; i++){
    v.push_back(i);
}

//print those element
for (auto it = 0; it < v.end(); i++){
    std::cout << *it << std::endl;
}

1

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

for(int x: A) // for integer x in vector A
    cout<< x <<" "; 

0
 //different declaration type
    vector<int>v;  
    vector<int>v2(5,30); //size is 5 and fill up with 30
    vector<int>v3={10,20,30};
    
    //From C++11 and onwards
    for(auto itr:v2)
        cout<<"\n"<<itr;
     
     //(pre c++11)   
    for(auto itr=v3.begin(); itr !=v3.end(); itr++)
        cout<<"\n"<<*itr;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.