2009-02-27 12 views
204

मैं कोड है कि इस तरह दिखता है मिल गया है:क्या आप इसके माध्यम से std :: सूची से तत्वों को हटा सकते हैं?

for (std::list<item*>::iterator i=items.begin();i!=items.end();i++) 
{ 
    bool isActive = (*i)->update(); 
    //if (!isActive) 
    // items.remove(*i); 
    //else 
     other_code_involving(*i); 
} 
items.remove_if(CheckItemNotActive); 

मैं, निष्क्रिय आइटम के तुरंत बाद उन्हें अपडेट हटाने inorder सूची फिर से चलने से बचना चाहते हैं। लेकिन अगर मैं टिप्पणी-आउट लाइन जोड़ता हूं, तो मुझे i++ पर एक त्रुटि मिलती है: "इटेटरेटर सूची बढ़ाना नहीं है"। मैंने कुछ विकल्पों की कोशिश की जो कथन में वृद्धि नहीं हुईं, लेकिन मुझे काम करने के लिए कुछ भी नहीं मिला।

आइटम को हटाने के लिए सबसे अच्छा तरीका क्या है जब आप std :: सूची चल रहे हैं?

उत्तर

227

आपको पहले इटरेटर को (i ++ के साथ) बढ़ाना होगा और फिर पिछले तत्व को हटा देना होगा (उदा।, I ++ से लौटाए गए मान का उपयोग करके)। तुम इतनी तरह थोड़ी देर के पाश करने के लिए कोड बदल सकते हैं:

std::list<item*>::iterator i = items.begin(); 
while (i != items.end()) 
{ 
    bool isActive = (*i)->update(); 
    if (!isActive) 
    { 
     items.erase(i++); // alternatively, i = items.erase(i); 
    } 
    else 
    { 
     other_code_involving(*i); 
     ++i; 
    } 
} 
+0

सही, एक आकर्षण की तरह काम किया। – AShelly

+4

दरअसल, यह काम करने की गारंटी नहीं है। "मिटाएं (i ++);", हम केवल इतना जानते हैं कि पूर्व-वृद्धिित मान मिटाया जाता है(), और मुझे सेमी-कोलन से पहले बढ़ाया जाता है, कॉल को मिटाए जाने से पहले जरूरी नहीं है()। "iterator prev = i ++; मिटाएं (पिछला);" काम करना सुनिश्चित है, जैसा रिटर्न वैल्यू –

+38

कोई जेम्स नहीं है, मुझे मिटाने से पहले बढ़ाया गया है, और पिछला मान फ़ंक्शन पर पास हो गया है। फ़ंक्शन कहने से पहले फ़ंक्शन के तर्कों का पूर्ण मूल्यांकन किया जाना चाहिए। –

102

आप क्या करना चाहते हैं:

i= items.erase(i); 

कि सही ढंग से स्थान इटरेटर के बाद आप निकाल दिया करने के लिए बात करने के लिए इटरेटर अद्यतन करेगा।

+64

का उपयोग करें चेतावनी दीजिये कि आप ' टी को उस कोड को अपने फॉर-लूप में छोड़ दें। अन्यथा जब भी आप एक को हटाते हैं तो आप एक तत्व छोड़ देंगे। –

+1

वह नहीं कर सकता i--; प्रत्येक बार छोड़ने से बचने के लिए कोड के टुकड़े का पालन करते हुए? – enthusiasticgeek

+1

@enthusiasticgeek, क्या होता है यदि i == items.begin() '? – MSN

9

std :: remove_if एल्गोरिदम का उपयोग करें।

संपादित करें: 1. संग्रह तैयार: की तरह संग्रह के साथ काम करने के लिए किया जाना चाहिए। 2. प्रक्रिया संग्रह।

यदि आप इन चरणों को मिश्रित नहीं करेंगे तो जीवन आसान होगा।

  1. std :: remove_if। या सूची :: remove_if (आप जानते हैं कि आप नहीं TCollection साथ सूची के साथ काम करते हैं और अगर)
  2. std :: for_each
+1

std :: सूची में सावधानी बरतने के लिए एक remove_if सदस्य फ़ंक्शन है जो remove_if एल्गोरिदम से अधिक कुशल है (और नहीं करता है "हटाने-मिटा" मुहावरे की आवश्यकता है)। –

2

हटाने केवल iterators है कि तत्वों कि निकाल दिए जाते हैं को इंगित अमान्य हो जाएगा।

तो इस मामले में * i को हटाने के बाद, मुझे अवैध कर दिया गया है और आप इसमें वृद्धि नहीं कर सकते हैं।

आप जो कर सकते हैं वह पहले तत्व के इटरेटर को हटाया जाता है जिसे हटाया जाना है, फिर इटरेटर को बढ़ाएं और फिर सहेजे गए को हटा दें।

+2

पोस्ट-वृद्धि का उपयोग करना कहीं अधिक सुरुचिपूर्ण है। –

18

आप Kristo के जवाब का संयोजन करने की ज़रूरत है और MSN के:

// This implementation of update executes other_code_involving(Item *) if 
// this instance needs updating. 
// 
// This method returns true if this still needs future updates. 
// 
bool Item::update(void) 
{ 
    if (m_needsUpdates == true) 
    { 
     m_needsUpdates = other_code_involving(this); 
    } 

    return (m_needsUpdates); 
} 

// This call does everything the previous loop did!!! (Including the fact 
// that it isn't deleting the items that are erased!) 
items.remove_if(std::not1(std::mem_fun(&Item::update))); 
+0

मैंने आपकी सुपरकूल विधि पर विचार किया था, मेरी हिचकिचाहट यह थी कि remove_if को कॉल करने से यह स्पष्ट नहीं होता है कि लक्ष्य सक्रिय लोगों की सूची से उन्हें हटाने के बजाय वस्तुओं को संसाधित करना है। (आइटम हटाए नहीं जा रहे हैं क्योंकि वे केवल निष्क्रिय हो रहे हैं, अनियंत्रित नहीं हैं) – AShelly

+0

मुझे लगता है कि आप सही हैं। एक ओर मैं अस्पष्टता को दूर करने के लिए 'अपडेट' के नाम को बदलने का सुझाव देने के इच्छुक हूं, लेकिन सच्चाई यह है कि यह कोड मज़दूरों के साथ फैंसी है, लेकिन यह गैर-अस्पष्ट भी है। – Mike

+0

आप 'इटेटरेटर एंड' को परिभाषित करते हैं लेकिन इसका कभी भी उपयोग नहीं करते ... – JHBonarius

4

विकल्प:

// Note: Using the pre-increment operator is preferred for iterators because 
//  there can be a performance gain. 
// 
// Note: As long as you are iterating from beginning to end, without inserting 
//  along the way you can safely save end once; otherwise get it at the 
//  top of each loop. 

std::list< item * >::iterator iter = items.begin(); 
std::list< item * >::iterator end = items.end(); 

while (iter != items.end()) 
{ 
    item * pItem = *iter; 

    if (pItem->update() == true) 
    { 
     other_code_involving(pItem); 
     ++iter; 
    } 
    else 
    { 
     // BTW, who is deleting pItem, a.k.a. (*iter)? 
     iter = items.erase(iter); 
    } 
} 
बेशक

, सबसे अधिक कुशल और अधिक सर्द ® एसटीएल savy बात कुछ इस तरह होगा क्रिस्टो के जवाब के लिए लूप संस्करण के लिए।

आप कुछ दक्षता खो देते हैं, आप पीछे जाते हैं और फिर हटाते समय फिर से आगे बढ़ते हैं लेकिन अतिरिक्त इटरेटर वृद्धि के बदले में आप लूप स्कोप में घोषित इटरेटर और कोड को थोड़ा क्लीनर देख सकते हैं। क्या चुनना है इस पल की प्राथमिकताओं पर निर्भर करता है।

उत्तर पूरी तरह से समय से बाहर था, मुझे पता है ...

typedef std::list<item*>::iterator item_iterator; 

for(item_iterator i = items.begin(); i != items.end(); ++i) 
{ 
    bool isActive = (*i)->update(); 

    if (!isActive) 
    { 
     items.erase(i--); 
    } 
    else 
    { 
     other_code_involving(*i); 
    } 
} 
+1

यही मैंने भी उपयोग किया है। लेकिन मुझे यकीन नहीं है कि अगर तत्व को हटाया जाना है तो यह काम करने की गारंटी है कंटेनर में पहला तत्व है। मेरे लिए, यह काम करता है, मुझे लगता है, लेकिन मुझे यकीन नहीं है कि यह प्लेटफॉर्म पर पोर्टेबल है या नहीं। – trololo

+0

मैंने "-1" नहीं किया, हालांकि, सूची इटरेटर की कमी नहीं हो सकती है? कम से कम मुझे विजुअल स्टूडियो 2008 से दावा मिला है। – milesma

+0

जब तक लिंक्ड सूची को एक गोलाकार डबल लिंक्ड सूची के रूप में लागू किया जाता है, जिसमें सिर/स्टब नोड (अंत() rbegin() के रूप में उपयोग किया जाता है और जब खाली के रूप में उपयोग किया जाता है() और rend() भी) यह काम करेगा। मुझे याद नहीं है कि मैं किस प्लेटफ़ॉर्म का उपयोग कर रहा था, लेकिन यह मेरे लिए भी काम कर रहा था, क्योंकि ऊपर नामित कार्यान्वयन std :: सूची के लिए सबसे आम कार्यान्वयन है। लेकिन वैसे भी, यह लगभग निश्चित है कि यह कुछ अपरिभाषित (सी ++ मानक) व्यवहार का शोषण कर रहा था, इसलिए इसका बेहतर उपयोग न करें। –

-4

मुझे लगता है कि तुम वहाँ एक बग है, मैं इस तरह से कोड डाल दें:

for (std::list<CAudioChannel *>::iterator itAudioChannel = audioChannels.begin(); 
      itAudioChannel != audioChannels.end();) 
{ 
    CAudioChannel *audioChannel = *itAudioChannel; 
    std::list<CAudioChannel *>::iterator itCurrentAudioChannel = itAudioChannel; 
    itAudioChannel++; 

    if (audioChannel->destroyMe) 
    { 
     audioChannels.erase(itCurrentAudioChannel); 
     delete audioChannel; 
     continue; 
    } 
    audioChannel->Mix(outBuffer, numSamples); 
} 
4

यहाँ एक for पाश कि सूची और वेतन वृद्धि iterates या किसी आइटम की स्थिति में इटरेटर revalidates का उपयोग कर एक उदाहरण है सूची के ट्रैवर्सल के दौरान हटाया जा रहा है।

for(auto i = items.begin(); i != items.end();) 
{ 
    if(bool isActive = (*i)->update()) 
    { 
     other_code_involving(*i); 
     ++i; 

    } 
    else 
    { 
     i = items.erase(i); 

    } 

} 

items.remove_if(CheckItemNotActive); 
2

आप std::list एक कतार की तरह के बारे में सोच है, तो आप विपंक्ति और सभी आइटम है कि आप रखना चाहते हैं enqueue सकते हैं, लेकिन केवल विपंक्ति (और enqueue नहीं) आइटम आप निकालना चाहते हैं। यहाँ एक उदाहरण है जहाँ मैं संख्या 1-10 से युक्त एक सूची से 5 निकालना चाहते है ...

std::list<int> myList; 

int size = myList.size(); // The size needs to be saved to iterate through the whole thing 

for (int i = 0; i < size; ++i) 
{ 
    int val = myList.back() 
    myList.pop_back() // dequeue 
    if (val != 5) 
    { 
     myList.push_front(val) // enqueue if not 5 
    } 
} 

myList अब केवल संख्या 1-4 और 6-10 होगा।

2

आप

std::list<item*>::iterator i = items.begin(); 
while (i != items.end()) 
{ 
    bool isActive = (*i)->update(); 
    if (!isActive) { 
     i = items.erase(i); 
    } else { 
     other_code_involving(*i); 
     i++; 
    } 
} 

लिख सकते हैं आप std::list::remove_if साथ बराबर कोड है, जो कम वर्बोज़ और अधिक स्पष्ट

items.remove_if([] (item*i) { 
    bool isActive = (*i)->update(); 
    if (!isActive) 
     return true; 

    other_code_involving(*i); 
    return false; 
}); 

std::vector::erasestd::remove_if मुहावरा इस्तेमाल किया जाना चाहिए जब आइटम के बजाय एक वेक्टर है है लिख सकते हैं ओ (एन) पर कॉम्पेक्सिटी रखने की एक सूची - या यदि आप जेनेरिक कोड लिखते हैं और आइटम एक कंटेनर हो सकते हैं जिसमें एकल आइटम (वेक्टर की तरह) को मिटाने का कोई प्रभावी तरीका नहीं है

items.erase(std::remove_if(begin(items), end(items), [] (item*i) { 
    bool isActive = (*i)->update(); 
    if (!isActive) 
     return true; 

    other_code_involving(*i); 
    return false; 
})); 

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

  • कोई संबंधित समस्या नहीं^_^