2009-06-13 11 views
25

मेरे पास एक वेक्टर है जो कई ऑब्जेक्ट्स को गतिशील रूप से तत्काल गतिशील करता है, और मैं वेक्टर के माध्यम से पुन: प्रयास करने की कोशिश कर रहा हूं और कुछ तत्वों को हटा सकता हूं (वेक्टर से हटाकर वस्तु को नष्ट कर सकता हूं), लेकिन मुझे परेशानी हो रही है।वेक्टर में संग्रहीत वस्तुओं को पॉइंटर्स को मिटाने और हटाने के लिए कैसे?

vector<Entity*> Entities; 
    /* Fill vector here */ 
    vector<Entity*>::iterator it; 
    for(it=Entities.begin(); it!=Entities.end(); it++) 
     if((*it)->getXPos() > 1.5f) 
      Entities.erase(it); 

जब इकाई वस्तुओं में से किसी एक अभिकथन त्रुटि के साथ xPos> 1.5, कार्यक्रम दुर्घटनाओं के लिए मिलता है ... किसी को भी पता है कि मैं गलत क्या कर रही हूं: यहाँ कि यह दिखता है?

मैं उपयोग कर रहा हूँ कुलपति ++ 2008

+0

कृपया अपने प्रश्नों को उस भाषा/पर्यावरण के साथ टैग करें जिसे आप उपयोग कर रहे हैं ताकि हम जान सकें कि आप मुख्य पृष्ठ से क्या उपयोग कर रहे हैं (और आपको कई और विचार मिलेंगे)। – Zifre

+0

लेकिन आप नहीं जानते कि वह अपने शेष कोड में वेक्टर के साथ क्या कर रहा है! आम तौर पर, एक वेक्टर पहला विकल्प कंटेनर होना चाहिए, अन्य चीजें बराबर होती हैं। –

+2

सामान्य रूप से, आपको हाथ से लिखित loops के लिए एसटीएल एल्गोरिदम पसंद करना चाहिए। – rlbond

उत्तर

37

आपको सावधान रहना होगा क्योंकि erase() मौजूदा iterators को अमान्य कर देगा। हालांकि, ir रिटर्न एक नया मान्य इटरेटर आप का उपयोग कर सकते हैं:

for (it = Entities.begin(); it != Entities.end();) 
    if((*it)->getXPos() > 1.5f) 
     delete * it; 
     it = Entities.erase(it); 
    } 
    else { 
     ++it; 
    } 
} 
+0

काम करने लगता है, हालांकि मुझे आश्चर्य है कि इस और केंड 64 के जवाब में क्या अंतर है? वेक्टर :: मिटाएं() ऑब्जेक्ट के विनाशक को कॉल करने का दावा करता है, इसलिए "इसे हटाएं"; ज़रूरी? –

+4

पॉइंटर्स के पास विनाशक नहीं हैं। वेक्टर में चीज के लिए विनाशक केवल तभी बुलाया जाएगा जब यह इकाई मूल्यों का संग्रह था। इसलिए यदि आप स्मृति रिसाव से बचना चाहते हैं तो हटाने के लिए कॉल आवश्यक है। –

+1

@ जीमैन मुझे लगता है कि आप मतलब है कि यह संकेतक है, सूचक नहीं। –

0

एक बार जब आप वेक्टर संशोधित करने, सभी बकाया iterators अमान्य हो। दूसरे शब्दों में, आप वेक्टर को संशोधित नहीं कर सकते हैं, जबकि आप इसके माध्यम से पुनरावृत्त कर रहे हैं। इस बारे में सोचें कि स्मृति में क्या करता है और आप देखेंगे क्यों। मुझे संदेह है कि आपका जोर एक "अवैध इटरेटर" जोर है।

std :: vector :: मिटाएं() एक पुनरावर्तक देता है जिसका उपयोग आप जिस व्यक्ति का उपयोग कर रहे थे उसे बदलने के लिए करना चाहिए। here देखें।

+0

मिट वास्तव में केवल मिटाए गए आइटम और तत्वों को इंगित करने वाले इटरेटर्स को अमान्य कर देता है, निचले तत्वों को इंगित करने वाले इटरेटर अमान्य नहीं हैं। – Dolphin

2
if((*it)->getXPos() > 1.5f) 
{ 
    delete *it; 
    it = Entities.erase(it); 
} 
+2

यह गलत है, क्योंकि मिटाए जाने के बाद इरेटरेटर मिटा दिया जाता है() मिटाए जाने के बाद बढ़ता जाता है। –

8

"सही" तरीका यह है एक कलन विधि का उपयोग किया जाता है:

#include <algorithm> 
#include <functional> 

// this is a function object to delete a pointer matching our criteria. 
struct entity_deleter 
{ 
    void operator()(Entity*& e) // important to take pointer by reference! 
    { 
     if (e->GetXPos() > 1.5f) 
     { 
      delete e; 
      e = NULL; 
     } 
} 

// now, apply entity_deleter to each element, remove the elements that were deleted, 
// and erase them from the vector 
for_each(Entities.begin(), Entities.end(), entity_deleter()); 
vector<Entity*>::iterator new_end = remove(Entities.begin(), Entities.end(), static_cast<Entity*>(NULL)); 
Entities.erase(new_end, Entities.end()); 

अब मुझे पता है कि तुम क्या सोच रहे हैं। आप सोच रहे हैं कि कुछ अन्य उत्तर कम हैं। लेकिन, (1) यह विधि आम तौर पर तेज़ कोड के लिए संकलित होती है - इसकी तुलना करने का प्रयास करें, (2) यह "उचित" एसटीएल तरीका है, (3) मूर्खतापूर्ण त्रुटियों के लिए कम मौका है, और (4) यह आसान है एक बार जब आप एसटीएल कोड पढ़ सकते हैं तो पढ़ें। यह एसटीएल प्रोग्रामिंग सीखने के लायक है, और मेरा सुझाव है कि आप स्कॉट मेयर की महान पुस्तक "प्रभावशाली एसटीएल" की जांच करें जिसमें इस तरह की चीजों पर एसटीएल युक्तियों का भार है।

एक और महत्वपूर्ण बात यह है कि ऑपरेशन के अंत तक तत्वों को मिटाने से, तत्वों को चारों ओर घूमने की आवश्यकता नहीं होती है। जीएमएन इससे बचने के लिए एक सूची का उपयोग करने का सुझाव दे रहा था, लेकिन इस विधि का उपयोग करके, पूरा ऑपरेशन ओ (एन) है। इसके विपरीत, नील का कोड ओ (एन^2) है, क्योंकि खोज ओ (एन) है और हटाने ओ (एन) है।

+0

मुझे नहीं किया गया था, और लिखा था कि शीर्ष पर कोड अगर पॉइंटर्स को हटाने की आवश्यकता नहीं है। कभी-कभी आप साझा स्वामित्व वाले पॉइंटर्स के कंटेनर चाहते हैं। – rlbond

+4

स्पष्ट लूप, आईएमएचओ से काफी कम स्पष्ट है। –

+0

और यह भी अधिक कोड है! –

0

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

एसटीएल कंटेनर एक विशेष कार्यान्वयन को लागू नहीं करते हैं, लेकिन एक वेक्टर आमतौर पर हुड के नीचे एक सरणी द्वारा लागू किया जाता है। यदि आप शुरुआत में कोई तत्व हटाते हैं, तो अन्य सभी आइटम स्थानांतरित हो जाते हैं। यदि आपके पास अन्य वस्तुओं में से किसी एक को इंगित करने वाला एक इटरेटर था तो यह अब पुराने तत्व के बाद तत्व पर इंगित कर सकता है। यदि आप कोई आइटम जोड़ते हैं, तो सरणी का आकार बदलना पड़ सकता है, इसलिए एक नई सरणी बनाई जाती है, पुरानी चीजें कॉपी हो जाती हैं, और अब आपका इटरेटर वेक्टर के पुराने संस्करण को इंगित कर रहा है, जो खराब है।

आपकी समस्या के लिए वेक्टर पीछे की ओर के माध्यम से फिर से सुरक्षित होना चाहिए और जब आप जाते हैं तो तत्वों को हटा दें। यह थोड़ा तेज भी होगा, क्योंकि आप उन वस्तुओं के आस-पास नहीं जा रहे हैं जिन्हें आप बाद में हटाने जा रहे हैं।

vector<Entity*> Entities; 
/* Fill vector here */ 
vector<Entity*>::iterator it; 
for(it=Entities.end(); it!=Entities.begin();){ 
    --it; 
    if(*(*it) > 1.5f){ 
    delete *it; 
    it=Entities.erase(it); 
    } 
} 
+0

जबकि पिछला पुनरावृत्ति चालाक है, आप दो समस्याओं में भागने जा रहे हैं। पहला, वेक्टर :: मिटाएं() सभी iterators को अमान्य करता है, इसलिए उपरोक्त आपका कोड एक अवैध इटरेटर का उपयोग करता है। लेकिन, अधिक दबाने वाली समस्या यह है कि वेक्टर :: मिटाएं() एक रिवर्स_इटरेटर स्वीकार नहीं करता है! आपके द्वारा ऊपर लिखा गया कोड संकलित नहीं होना चाहिए। – rlbond

+0

हम्म, एक रिवर्स_इटरेटर नहीं लेना मिटाना एक समस्या हो सकती है :) लेकिन, मिटाने से पहले एलिमेंट तत्वों को इंगित करने वाले इटरेटर्स को अमान्य नहीं किया जाता है। किसी भी तरह से, संकलन और काम कोड के साथ तय किया। – Dolphin

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