2011-04-15 20 views
12

मुझे यह कैसे करना है इसका एक उदाहरण नहीं मिला, इसलिए मुझे उम्मीद थी कि कोई मेरी मदद कर सकता है। मेरे पास एक वर्ग में परिभाषित नक्शा है:सी ++ हटाने के साथ मानचित्र पुनरावृत्ति

std::map<std::string, TranslationFinished> translationEvents; 

अनुवाद समाप्त एक बढ़ावा :: समारोह है। मैं अपने वर्ग का हिस्सा के रूप में एक विधि है कि इस नक्शे के माध्यम से दोहराता है, तो जैसे कार्यों में से प्रत्येक बुला:

void BaseSprite::DispatchTranslationEvents() 
{ 
    for(auto it = translationEvents.begin(); it != translationEvents.end(); ++it) 
    { 
     it->second(this); 
    } 
} 

हालांकि यह एक समारोह it->second(this); द्वारा बुलाया के लिए संभव है translationEvents नक्शे से एक तत्व को दूर करने के लिए (आमतौर पर ही) निम्न फ़ंक्शन का उपयोग:

bool BaseSprite::RemoveTranslationEvent(const std::string &index) 
{ 
    bool removed = false; 
    auto it = translationEvents.find(index); 
    if (it != translationEvents.end()) 
    { 
     translationEvents.erase(it); 
     removed = true; 
    } 
    return removed; 
} 

कर इस जब DispatchTranslationEvents() की कोशिश करता इटरेटर को बढ़ाने के लिए एक डिबग दावे विफल कारण बनता है। क्या मानचित्र के माध्यम से पुनरावृत्ति के दौरान एक फ़ंक्शन कॉल मानचित्र से तत्व को हटा सकता है, इस संभावना के साथ सुरक्षित रूप से मानचित्र के माध्यम से फिर से प्रयास करने का कोई तरीका है?

अग्रिम धन्यवाद

संपादित करें: Accidently सी/पीडी गलत निकालें घटना कोड। अब तय

उत्तर

4

अन्य सभी उत्तरों पढ़ने के बाद, मैं यहां एक लाभ में हूं ... लेकिन यहां यह जाता है।

हालांकि यह इसके द्वारा बुलाए गए फ़ंक्शन के लिए संभव है-> दूसरा (यह); translationEvents नक्शा (आमतौर पर ही)

अगर यह सही है, कि है, से एक तत्व को दूर करने के एक कॉलबैक कंटेनर से किसी भी तत्व निकाल सकते हैं, आप संभवतः पाश से ही इस समस्या को हल नहीं कर सकते।

वर्तमान कॉलबैक

हटाया जा रहा है सरल मामले में जहां कॉलबैक केवल खुद को हटा सकते हैं में, आप विभिन्न तरीकों का उपयोग कर सकते हैं:

// [1] Let the callback actually remove itself 
for (iterator it = next = m.begin(); it != m.end(); it = next) { 
    ++next; 
    it->second(this); 
} 
// [2] Have the callback tell us whether we should remove it 
for (iterator it = m.begin(); it != m.end();) { 
    if (!it->second(this)) {     // false means "remove me" 
     m.erase(it++); 
    } else { 
     ++it; 
    } 
} 

इन दो विकल्पों के अलावा, मैं स्पष्ट रूप से पसंद करेंगे [2 ], क्योंकि आप हैंडलर के कार्यान्वयन से कॉलबैक को डीकॉप्लिंग कर रहे हैं। यही है, [2] में कॉलबैक उस कंटेनर के बारे में कुछ भी नहीं जानता जिसमें यह आयोजित किया जाता है। [1] में एक उच्च युग्मन है (कॉलबैक कंटेनर के बारे में जानता है) और कोड के कई स्थानों से कंटेनर बदल दिया गया है, इसके बारे में तर्क करना मुश्किल है।कुछ समय बाद आप भी कोड पर वापस लग सकता है, लगता है कि यह एक अजीब पाश है (याद नहीं है कि कॉलबैक खुद को हटाता है) और अधिक कुछ में यह refactor समझदारfor (auto it = m.begin(), end = m.end(); it != end; ++it) it->second(this); रूप

अन्य कॉलबैक

हटाया जा रहा है

की अधिक जटिल समस्या के लिए कोई अन्य कॉलबैक हटा सकता है, यह सब आपके द्वारा किए गए समझौतों पर निर्भर करता है। सरल मामले में, जहां यह केवल पूरा यात्रा के बाद अन्य कॉलबैक को हटा में, तो आपको एक अलग सदस्य समारोह है कि तत्वों को दूर करने के रखना होगा प्रदान कर सकते हैं, और फिर उन्हें एक बार में सभी को दूर करने के बाद पाश पूरे करता है:

void removeElement(std::string const & name) { 
    to_remove.push_back(name); 
} 
... 
for (iterator it = m.begin(); it != m.end(); ++it) { 
    it->second(this);  // callback will possibly add the element to remove 
} 
// actually remove 
for (auto it = to_remove.begin(); it != to_begin.end(); ++it) { 
    m.erase(*it); 
} 

यदि तत्वों को हटाने की तत्काल आवश्यकता है (यानी उन्हें इस पुनरावृत्ति में भी नहीं कहा जाना चाहिए यदि उन्हें अभी तक नहीं कहा गया है), तो आप यह जांच करके उस दृष्टिकोण को संशोधित कर सकते हैं कि कॉल को निष्पादित करने से पहले इसे हटाने के लिए चिह्नित किया गया था या नहीं। निशान दो तरीकों से किया जा सकता है, जिसमें से जेनेरिक कंटेनर में मान प्रकार को pair<bool,T> होने के लिए बदल देगा, जहां बूल इंगित करता है कि यह जीवित है या नहीं। , तो इस मामले में के रूप में, निहित वस्तु बदला जा सकता है आप सिर्फ इतना है कि कर सकता है:

void removeElement(std::string const & name) { 
    auto it = m.find(name);   // add error checking... 
    it->second = TranslationFinished(); // empty functor 
} 
... 
for (auto it = m.begin(); it != m.end(); ++it) { 
    if (!it->second.empty()) 
     it->second(this); 
} 
for (auto it = m.begin(); it != m.end();) { // [3] 
    if (it->second.empty()) 
     m.erase(it++); 
    else 
     ++it; 
} 

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

6

आम तौर पर यह पुनरावृत्ति के दौरान संग्रह को संशोधित करने के लिए तैयार है। संग्रह में संशोधित होने पर कई संग्रह इटरेटर को अमान्य करते हैं, जिसमें सी # में कई कंटेनर शामिल हैं (मुझे पता है कि आप सी ++ में हैं)। आप पुनरावृत्ति के दौरान हटाए जाने वाले ईवेंट का वेक्टर बना सकते हैं और फिर उन्हें बाद में हटा सकते हैं।

+0

सी # के संग्रह कक्षाएं इस संबंध में भयानक हैं। अगर वे संशोधित होते हैं तो वे * सबकुछ * को अमान्य करते हैं। सी ++ कंटेनर आमतौर पर इस संबंध में बहुत बेहतर व्यवहार करते हैं, और आम तौर पर केवल विशिष्ट इटरेटर्स को अमान्य कर देते हैं, ताकि आप – jalf

2

आपके पुनरावृत्ति के दौरान तत्व को मिटाने का एक तरीका होना चाहिए, शायद थोड़ा मुश्किल।

for(auto it = translationEvents.begin(); it != translationEvents.end();) 
{ 
    //remove the "erase" logic from second call 
    it->second(this); 
    //do erase and increase the iterator here, NOTE: ++ action is very important 
    translationEvents.erase(it++);   
} 

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

+0

पुनरावृत्ति के दौरान मिटाने के दौरान हटा सकें, एक सामान्य पैटर्न है, कॉलबैक के साथ आमतौर पर रिटर्न वैल्यू के साथ कार्यान्वित किया जाता है जो हटाने की आवश्यकता को स्थापित करता है : 'अगर (यह-> दूसरा (यह)) {translationEvents.erase (यह ++); } else ++ it', जहां प्रत्येक कॉलबैक 'सत्य' वापस कर देगा यदि यह निष्पादन के बाद मान्य है। –

1

समस्या ++it संभावित मिटा के बाद है। क्या यह आपके लिए काम करेगा?

void BaseSprite::DispatchTranslationEvents() 
{ 
    for(auto it = translationEvents.begin(), next = it; 
     it != translationEvents.end(); it = next) 
    { 
     next=it; 
     ++next; 
     it->second(this); 
    } 
} 
+1

प्रश्न बताता है कि वर्तमान के अलावा कुछ तत्व को हटाना संभव है। यदि अगला तत्व हटा दिया गया है, तो यह वास्तव में काम नहीं करेगा। – Anton

3

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

void BaseSprite::DispatchTranslationEvents() 
{ 
    typedef std::map<std::string, TranslationFinished> container_t; 

    container_t tempEvents; 
    tempEvents.swap(translationEvents); 

    for(auto it = tempEvents.begin(); it != tempEvents.end(); ++it) 
    { 
     if (true == it->second(this)) 
      translationEvents.insert(it); 
    } 
} 

और TranslationFinished कार्यों सच वापसी अगर यह keeped होना चाहते हैं और समाप्त करने के लिए झूठी लौटना चाहिए।

bool BaseSprite::RemoveTranslationEvent(const std::string &index) 
{ 
    bool keep = false; 
    return keep; 
} 
2

आप प्रेषण लूप जब तक हटाने को स्थगित कर सकता:

typedef boost::function< some stuff > TranslationFunc; 

bool BaseSprite::RemoveTranslationEvent(const std::string &index) 
{ 
    bool removed = false; 
    auto it = translationEvents.find(index); 
    if (it != translationEvents.end()) 
    { 
     it->second = TranslationFunc(); // a null function indicates invalid event for later 
     removed = true; 
    } 
    return removed; 
} 

पाश अपने आप में एक अमान्य ईवेंट लागू के खिलाफ की रक्षा, और सफाई किसी भी "हटा" घटनाओं:

void BaseSprite::DispatchTranslationEvents() 
{ 
    for(auto it = translationEvents.begin(); it != translationEvents.end();) 
    { 
     // here we invoke the event if it exists 
     if(!it->second.empty()) 
     { 
      it->second(this); 
     } 

     // if the event reset itself in the map, then we can cleanup 
     if(it->second.empty()) 
     { 
      translationEvents.erase(it++); // post increment saves hassles 
     } 
     else 
     { 
      ++it; 
     } 
    } 
} 

एक स्पष्ट चेतावनी यह है कि यदि कोई ईवेंट पुन: चालू हो जाता है, और उसके बाद बाद में हटा दिया जाता है, तो उसे वर्तमान प्रेषण लूप के दौरान फिर से हटाए जाने का मौका नहीं मिलेगा।

इसका मतलब है कि अगली बार प्रेषण लूप चलाए जाने तक उस घटना का वास्तविक विलोपन स्थगित कर दिया जाएगा।

+0

+1, इस विचार को दूसरे कंटेनर (कॉलबैक के संदर्भ में पारित) का उपयोग करके बढ़ाया जा सकता है, जहां वे हटाए जाने की आवश्यकता होने पर खुद को एक इटरेटर (या अन्य इटरेटर) डालते हैं। निष्पादन लूप पूरा होने के बाद, आप उम्मीदवारों की सूची को हटाने और उन्हें बेदखल करने के लिए पुन: प्रयास कर सकते हैं। कॉलबैक का वास्तविक इंटरफ़ेस कंटेनर रखने वाले ऑब्जेक्ट में केवल 'markForDeletion' विधि जोड़कर अपरिवर्तित रह सकता है। –

7

map::erase इटरेटर को हटाया जा रहा है (जाहिर है), लेकिन शेष मानचित्र नहीं। इसका मतलब है कि:

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

    for(auto it = translationEvents.begin(); it != translationEvents.end();) 
    { 
        auto next = it; 
        ++next; // get the next element 
        it->second(this); // process (and maybe delete) the current element 
        it = next; // skip to the next element 
    } 
    

    अन्यथा: std::map की तो आप बस इस तरह पाश पुनर्लेखन सकता है, नहीं है तो आप आप ही कभी वर्तमान तत्व हटाना मान लिया जाये कि इस मैन्युअल

करने के लिए) है, (यदि फ़ंक्शन किसी भी तत्व को हटा सकता है) यह थोड़ा और जटिल हो सकता है।

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