2010-11-23 16 views
13

क्या मैं std :: सूची से तत्वों को हटा सकता हूं, जब मैं इसे पुन: सक्रिय कर रहा हूं? उदाहरण के लिए:क्या मैं std :: सूची से तत्वों को हटा सकता हूं, जब मैं इसे पुन: सक्रिय कर रहा हूं?

std::list<int> lst; 
//.... 
for (std::list<int> itr = lst.begin(); itr != lst.end(); itr++) 
{ 
    if (*itr > 10) 
     lst.remove(*itr); 
} 

? और क्यों?

+1

[एक एसटीएल सूची से मिटाया जा रहा है आइटम] के संभावित डुप्लिकेट (http://stackoverflow.com/questions/501962/erasing-items-from-an-stl-list) – sth

+0

हाँ, पर उस तरह से नहीं। –

+0

संभावित डुप्लिकेट [क्या आप इसे std :: सूची से तत्वों को हटाते समय हटा सकते हैं?] (Http://stackoverflow.com/questions/596162/can-you-remove-elements-from-a-stdlist-while- iterating-through-it) – AShelly

उत्तर

29

सही कोड है निम्नलिखित:

for (std::list<int>::iterator itr = lst.begin(); itr != lst.end(); /*nothing*/) 
{ 
    if (*itr > 10) 
     itr = lst.erase(itr); 
    else 
     ++itr; 
} 

जब आप सूची से कोई आइटम हटाते हैं, तो आप इटरेटर अमान्य हो सकती है इसलिए आप erase का उपयोग कर हटाने की आवश्यकता (अगर यह कहते हैं करने के लिए आइटम हटाया जा रहा है।) (जो अगले आइटम को इंगित करने वाला एक मान्य इटरेटर देता है)।

और भी बेहतर विचार std::remove_if का उपयोग करेंगे:

bool greater_than_10(int x) 
{ 
    return x > 10; 
} 

lst.remove_if(greater_than_10); 

अपने संकलक lambdas का समर्थन करता है, तो आप इसे रख सकते हैं भी कम:

lst.remove_if([](int x){ return x > 10; }); 

(मैं इस कोड का परीक्षण नहीं किया है, के रूप में मेरे कंपाइलर इतना नया नहीं है; लैम्ब्डा फ़ंक्शन को जॉन जॉनिंग के उत्तर से धन्यवादपूर्वक चोरी किया जाता है।)


असल में, सूची से मिटाने से only the iterators pointing to the item being deleted अमान्य हो जाता है। सावधान रहें कि अन्य एसटीएल कंटेनरों में यह संपत्ति नहीं है।


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

सावधान रहें कि अन्य एसटीएल कंटेनर (उदा। वैक्टर) के लिए बाधा अधिक सख्त है: कंटेनर से हटाना न केवल हटाए गए आइटम को इंगित करने वाले इटरेटर को अमान्य करता है, बल्कि संभवतः अन्य इटरेटर भी! तो उन कंटेनरों से हटने के दौरान उन्हें और भी समस्याग्रस्त हो रहा है।

+0

हा। जब दो एसओ लोग लगभग समान उदाहरण कोड पोस्ट करते हैं, तो यह एक अच्छा विचार होना चाहिए। – aschepler

+0

@aschelper: वास्तव में ;-) – Vlad

+0

ओह, मैंने एक बुरा उदाहरण दिया। मैं सिर्फ विधि को हटाने का उपयोग करना चाहता हूं (यदि कुछ निश्चित स्थिति के साथ मैं कुछ मूल्यों के साथ सभी आइटम हटाना चाहता हूं)। –

0

मुझे लगता है कि आप कर सकते हैं, लेकिन तत्व को हटाने के बाद आपको पुनरावर्तक को फिर से सौंपना होगा, विधि के साथ remove के साथ किया जा सकता है।

अन्यथा यह असुरक्षित होगा और नहीं किया जाना चाहिए।

8

नहीं। उदाहरण कोड itr को अमान्य करता है, जो अपरिभाषित व्यवहार को जन्म देता है। लेकिन यह काम करेगा:

for (std::list<int>::iterator itr = lst.begin(); itr != lst.end();) 
{ 
    if (*itr > 10) 
     itr = lst.erase(itr); 
    else 
     ++itr; 
} 
+0

इटेटरेटर को पूर्व-वृद्धि करना एक अच्छा विचार है; मैंने मैच करने के लिए अपना कोड बदल दिया। अब उदाहरण कोड बिल्कुल वही है :) – Vlad

3

नहीं, आप नहीं कर सकते।

लेकिन आप (और चाहिए) एक functor कि कहते हैं, "एक से अधिक 10`, इस तरह के साथ std::remove_if उपयोग कर सकते हैं:

#include <list> 
#include <algorithm> 


int main() 
{ 
    std::list<int> lst; 
    lst.push_back(1); 
    lst.push_back(12); 
    lst.push_back(1); 
    //.... 
    lst.erase(std::remove_if(lst.begin(), lst.end(), std::bind2nd(std::greater<int>(), 10)), lst.end()); 
} 

एक और, यह करने के लिए और अधिक सामान्य तरीके से अपने स्वयं के कस्टम functor लिखना है यहां एक मजेदार is_a_match है जो true देता है यदि मूल्य की जांच 10 से अधिक है।आप operator() को फिर से परिभाषित कर सकते हैं true वापस जाने के लिए जो कुछ भी यह "मैच" करने के लिए अपने मामले में इसका मतलब के अनुरूप करने के लिए:

#include <list> 
#include <algorithm> 
#include <functional> 

struct is_a_match : public std::unary_function<int, bool> 
{ 
    is_a_match(int val) : val_(val) {}; 
    bool operator()(int victim) const { return victim > val_; } 
private: 
    int val_; 
}; 

int main() 
{ 
    std::list<int> lst; 
    lst.push_back(1); 
    lst.push_back(12); 
    lst.push_back(1); 
    //.... 
    lst.erase(std::remove_if(lst.begin(), lst.end(), is_a_match(10))); 
} 

आप एक C++ 0x अनुरूप संकलक के लाभ है, तो आप भी lambdas उपयोग कर सकते हैं, जो बनाता है यह संभव functor से छुटकारा पाने और कई मामलों

#include <list> 
#include <algorithm> 

int main() 
{ 
    std::list<int> lst; 
    lst.push_back(1); 
    lst.push_back(12); 
    lst.push_back(1); 
    //.... 
    lst.erase(std::remove_if(lst.begin(), lst.end(), [](int v) {return v > 10;})); 
} 
+0

दरअसल, 'सूची' में इसका अपना' remove_if' सदस्य फ़ंक्शन है: 'lst.remove_if (std :: bind2nd (std :: अधिक (), 10)); –

+0

@ फ्रेड: सच है, लेकिन यह अधिक सामान्य है और सिखाता है कि 'सूची' के अलावा संग्रह के लिए इसे कैसे किया जाए। मैं स्कॉट मेयर्स के विरोध में और सामान्य विचारों के विस्तार के रूप में गैर-सदस्य कार्य भी पसंद करता हूं। –

+0

@ जॉन: सहमत। मुझे लगता है कि 'सूची' इस तरह के कुछ सदस्य कार्यों (और 'sort', आदि) प्रदान करती है क्योंकि वे सूचियों के लिए अधिक अनुकूल हैं। यह मिटाने/निकालने के मुकाबले भी साफ दिखता है। लेकिन मैं आपके बिंदु की सराहना करता हूं। –

0

में अधिक अर्थपूर्ण कोड लिखने iterators के वर्णन के लिए http://www.cppreference.com/wiki/iterator/start देखने के लिए।

कुछ नोट:

  • आप के बाद वेतन वृद्धि ऑपरेटर के बजाय पूर्व वेतन वृद्धि ऑपरेटर (++itr) का उपयोग किया जाना चाहिए (itr++)
  • शक्ति कम इटरेटर का सही कार्यान्वयन पर निर्भर करता है और इसके संबंधित संग्रह।
संबंधित मुद्दे