iterators जो अपरिभाषित व्यवहार करने के लिए नेतृत्व के साथ संचालन की एक संख्या है, इस ट्रिगर के लक्ष्य यह घटित होने से बचाने के क्रम चेकों को सक्रिय करने के (का उपयोग करते हुए इस बात पर ज़ोर) है।
मुद्दा
स्पष्ट आपरेशन गलत iterator उपयोग करने के लिए है, लेकिन इस अमान्यता विभिन्न कारणों से उत्पन्न हो सकती:
- Unitialized इटरेटर
- एक तत्व है कि मिटा दिया गया है करने के लिए इटरेटर
- भौतिक स्थान बदलने वाले तत्व के लिए इटरेटर (
vector
के लिए पुनर्वितरण)
प्रत्येक कंटेनर जो आपरेशन को अमान्य कर जो इटरेटर के लिए कष्टदायी विवरण में मानक सटीक के बाहर 10
इटरेटर। वहाँ एक किसी भी तरह कम स्पष्ट कारण यह है कि लोग भूल जाते हैं के लिए करते हैं है: विभिन्न कंटेनरों को iterators मिश्रण:
std::vector<Animal> cats, dogs;
for_each(cats.begin(), dogs.end(), /**/); // obvious bug
यह एक अधिक सामान्य मुद्दे से संबंधित: पर्वतमाला एल्गोरिदम के लिए पारित की वैधता।
[cats.begin(), dogs.end())
अमान्य है
[cats.end(), cats.begin())
अमान्य है (जब तक cats
खाली है ??)
समाधान
समाधान होते हैं (जब तक कि एक दूसरे के लिए एक उपनाम है) इटरेटर को जानकारी जोड़ने में ताकि उनकी वैधता और परिभाषित श्रेणियों की वैधता निष्पादन के दौरान ज़ोर दी जा सके, इस प्रकार अपरिभाषित को रोकें व्यवहार होने के लिए।
_HAS_ITERATOR_DEBUGGING
प्रतीक इस क्षमता के लिए एक ट्रिगर के रूप में कार्य करता है, क्योंकि दुर्भाग्य से यह प्रोग्राम धीमा हो जाता है। सिद्धांत में यह काफी सरल है: प्रत्येक इटरेटर को Observer
बनाया गया कंटेनर बनाया गया है और इस प्रकार इसे संशोधित किया गया है।
Dinkumware में यह दो अतिरिक्त द्वारा हासिल की है:
- प्रत्येक इटरेटर इससे संबंधित कंटेनर के लिए एक सूचक किया जाता है
- प्रत्येक कंटेनर यह बनाया
iterators और यह की एक लिंक्ड सूची रखती है अच्छी तरह से हमारी समस्याओं को हल करता है:
- एक प्रारंभिक इटरेटर में कोई नहीं है पैरेंट कंटेनर, सबसे परिचालन (अलग काम और विनाश से) ट्रिगर किया जाएगा एक अभिकथन
- एक मिट या स्थानांतरित तत्व के लिए एक iterator (सूची के लिए धन्यवाद) अधिसूचित किया गया है और इसके अमान्यता
- के बारे में पता incrementing और पुनरावर्तक decrementing पर यह जांच कर सकता है कि यह सीमा
- जांच कर रहा है कि 2 पुनरावर्तक एक ही कंटेनर से संबंधित हैं, उनके माता-पिता पॉइंटर्स की तुलना में उतना आसान है
- किसी सीमा की वैधता की जांच करना उतना आसान है जितना कि हम सीमा के अंत तक पहुंचते हैं इससे पहले कि हम कंटेनर के अंत तक पहुंचें (उन कंटेनर के लिए रैखिक ऑपरेशन जो यादृच्छिक रूप से सुलभ नहीं हैं, इस प्रकार उनमें से अधिकतर)
लागत
लागत भारी है, लेकिन शुद्धता है एक कीमत है? परिवर्तनशील कार्यों पर O(NbIterators)
सूचना प्रक्रिया:
- अतिरिक्त स्मृति आवंटन (बनाए रखा iterators की अतिरिक्त सूची): हम लागत हालांकि बंद कर सकता है
O(NbIterators)
(ध्यान दें कि push_back
या insert
करना जरूरी नहीं कि iterators को अमान्य कर, लेकिन erase
करता है)
- रेंज वैधता की जांच:
O(min(last-first, container.end()-first))
पुस्तकालय एल्गोरिदम के अधिकांश निश्चित रूप से लागू किया गया है च या अधिकतम दक्षता, आम तौर पर चेक एक बार और एल्गोरिदम की शुरुआत में किया जाता है, फिर एक अनचेक संस्करण चलाया जाता है।फिर भी गति गंभीर रूप से, धीमा हो सकता है विशेष रूप से हाथ से लिखा छोरों के साथ:
for (iterator_t it = vec.begin();
it != vec.end(); // Oups
++it)
// body
हम जानते हैं Oups लाइन खराब स्वाद है, लेकिन यहाँ तो यह और भी बदतर है: पाश के प्रत्येक रन पर, हम एक नया बनाने इटरेटर तो इसे नष्ट आवंटन और iterators की vec
की सूची के लिए एक नोड deallocating जिसका मतलब है ... मैं आवंटन/एक तंग पाश में स्मृति deallocating की लागत को रेखांकित किया है?
बेशक, for_each
ऐसी समस्या का सामना नहीं करेगा, जो हाथ से कोडित संस्करणों के बजाय एसटीएल एल्गोरिदम के उपयोग की ओर एक और आकर्षक मामला है।
एक तरफ ध्यान दें पर, मुझे आश्चर्य है कि कितने लोगों को वास्तव में पता है कि Dinkumware एसटीएल :) – sbk
है तुम नहीं जानते, तो आप शायद इस सवाल का जवाब नहीं कर सकते हैं :-) – Roddy