2009-04-28 11 views
43

को हटाने के बीच अंतर मैं std :: हटाने एल्गोरिदम के उपयोग के बीच अंतर के बारे में थोड़ा उलझन में हूं। विशेष रूप से मैं समझ नहीं पा रहा हूं कि जब मैं इस एल्गोरिदम का उपयोग करता हूं तो क्या हटाया जा रहा है। मैंने इस तरह एक छोटा परीक्षण कोड लिखा:मिटाने और

std::vector<int> a; 
a.push_back(1); 
a.push_back(2); 

std::remove(a.begin(), a.end(), 1); 


int s = a.size(); 

std::vector<int>::iterator iter = a.begin(); 
std::vector<int>::iterator endIter = a.end(); 

std::cout<<"Using iter...\n"; 
for(; iter != endIter; ++iter) 
{ 
    std::cout<<*iter<<"\n"; 
} 

std::cout<<"Using size...\n"; 
for(int i = 0; i < a.size(); ++i) 
{ 
    std::cout<<a[i]<<"\n"; 
} 

दोनों मामलों में उत्पादन 2,2 था।

हालांकि, अगर मैं इस तरह निकालने के कुछ के साथ मिटा का उपयोग करें:

a.erase(std::remove(a.begin(), a.end(), 1), a.end()); 

मैं 2.

के रूप में आउटपुट प्राप्त तो मेरी प्रश्न हैं:

(1)। क्या erd फ़ंक्शन के साथ इसका उपयोग करने के अलावा std :: का कोई उपयोग है।

(2)। Std :: हटाने के बाद भी, क्यों a.size() 2 देता है और 1 नहीं?

मैंने स्कॉट मेयर की प्रभावशाली एसटीएल पुस्तक में मिटाने-हटाने के लिए आइटम को पढ़ा। लेकिन अभी भी यह भ्रम है।

+2

मुझे लगता है इस बारे में सबसे कठिन हिस्सा मिटा सकते हैं और हटाने का मतलब लगभग अंग्रेजी में बात बचाने के है, इसलिए इसकी आसान जो एक जो –

उत्तर

47

remove() वास्तव में कंटेनर से तत्वों को हटा नहीं देता है - यह केवल हटाए गए तत्वों के शीर्ष पर गैर-हटाए गए तत्वों को आगे बढ़ाता है। , यह नहीं वास्तव में तत्वों को नष्ट कर सकते इसका मतलब है कि एक मनमाना इटरेटर जोड़ी जरूरी नहीं है क्योंकि: कुंजी को एहसास है कि remove() किसी भी मनमाने ढंग से आगे इटरेटर जोड़ी न सिर्फ एक कंटेनर पर लेकिन पर काम करने के लिए बनाया गया है तत्वों को हटाने की क्षमता।

उदाहरण के लिए, शुरुआत और एक नियमित रूप से सी सरणी के अंत की ओर इशारा आगे iterators कर रहे हैं और के रूप में ऐसी remove() के साथ प्रयोग किया जा सकता है:

int foo[100]; 

... 

remove(foo, foo + 100, 42); // Remove all elements equal to 42 

यहाँ यह स्पष्ट है कि remove() सरणी का आकार नहीं कर सकते हैं!

17

std::remove वास्तविक वस्तुओं को नहीं हटाता है, बल्कि उन्हें कंटेनर के अंत तक धक्का देता है। मिटाने के माध्यम से स्मृति का वास्तविक विलोपन और विलोपन किया जाता है। तो:

(1)। क्या erd फ़ंक्शन के साथ इसका उपयोग करने के अलावा std :: का कोई उपयोग है।

हाँ, यह उचित de-आवंटन के बारे में चिंता किए बिना एक नया अनुक्रम को iterators की एक जोड़ी पाने के लिए मदद करता है आदि

(2)। Std :: हटाने के बाद भी, क्यों a.size() 2 देता है और 1 नहीं?

कंटेनर अभी भी उन वस्तुओं को रखता है, आपके पास केवल काम करने वालों के साथ काम करने का एक नया सेट है। इसलिए आकार अभी भी क्या होता है।

+4

हम्म है भूल जाते हैं। असल में std :: remove() हटाए गए तत्वों को कंटेनर के अंत में नहीं ले जाता है - कंटेनर की शेष स्थितियों में उनके मूल मान होंगे। (यह संरक्षित करने के लिए हे (एन) आगे iterators के साथ समय इस तरह से काम करना चाहिए।) –

+0

j_random_hacker, हम्म वास्तव में ऐसा होने के बाद यह काम करते हैं, सीमा के बाद लौट आए इटरेटर भी किसी भी "हटा" तत्व शामिल नहीं करना चाहिए किया लग रहा है - जो है एक अतिरिक्त आवश्यकता जिसे मैंने अभी तक नहीं माना है - इसलिए वे तत्व पूरी तरह से अनिश्चित नहीं हैं। तो मेरा जवाब गलत था और मैंने इसे –

+1

हटा दिया, यह कहता है: "रेजिडेंट [पहले, आखिरी) में संदर्भित सभी तत्वों को हटा देता है जिसके लिए निम्नलिखित संबंधित स्थितियां हैं: * i == मान" जिसे मैं समझता हूं उस सीमा में अंत में कोई भी "हटाया गया" मूल्य नहीं है। दिलचस्प है, मुझे यह नहीं पता है। लेकिन मुझे नहीं लगता कि कोई उन तत्वों पर भरोसा कर सकता है जो पहले जैसा ही है। एक निश्चित रूप से नहीं कर सकता है कि उस सीमा में कम से कम "हटाया गया" मान होगा। तो मुझे लगता है कि cplusplus.com फिर से गलत है आईएमओ: डी –

6

सरल मैं के साथ आ सकते हैं:

erase() कुछ आप एक कंटेनर में एक तत्व के लिए कर सकते है। एक कंटेनर में एक इटरेटर/इंडेक्स को देखते हुए, erase(it) उस चीज़ को हटा देता है जिस पर इटरेटर कंटेनर से संदर्भित होता है।

remove() कुछ आप एक श्रृंखला के लिए क्या कर सकते हैं, इसे फिर से व्यवस्था है कि सीमा लेकिन सीमा से कुछ भी नहीं मिटा है।

2

हटाएं "वास्तव में" कुछ भी नहीं हटाता है, क्योंकि यह नहीं कर सकता है।

"वास्तव में" कंटेनर से तत्वों को हटाने के लिए आपको कंटेनर एपीआई तक पहुंचने की आवश्यकता है। जहां हटाएं केवल इटरेटर के साथ काम करता है चाहे वे कंटेनर किस कंटेनर को इंगित करते हैं। इसलिए, अगर हटाएं "वास्तविक निकालना" चाहता है, तो यह नहीं हो सकता है।

हटाए गए निम्नलिखित तत्वों द्वारा "हटाए गए" तत्वों को ओवरराइट करें निकालें और फिर मूल end के बदले नए लॉजिकल end का उपयोग करने का निर्णय लेने के लिए कॉलर पर निर्भर है।

अपने मामले में को vector से तार्किक रूप से हटा दिया गया है लेकिन आकार 2 ही बना हुआ है। मिट वास्तव में वेक्टर से तत्व हटा दिया। [वेक्टर new end से old end]

remove का मुख्य विचार यह तत्वों की संख्या को नहीं बदला जा सकता है और यह मानदंडों के अनुसार केवल सीमा से तत्वों को हटा देता है।

6

मुझे अंतर को समझने की कोशिश कर, एक ही समस्या का सामना करना पड़ा। स्पष्टीकरण कि दे दिया है अब तक सही पैसे पर कर रहे हैं, लेकिन मैं केवल उन्हें एक उदाहरण देखने के बाद समझ में आ;

#include <algorithm> 
#include <string> 
#include <iostream> 
#include <cctype> 

int main() 
{ 
    std::string str1 = "Text with some spaces"; 
    std::string::iterator it = remove(str1.begin(), str1.end(), 't'); 
    std::cout << str1 << std::endl;// prints "Tex wih some spaceses" 
    for (str1.begin();it != str1.end(); ++it) 
    { 
     std::cout << *it; //prints "es" 
    } 

} 

के रूप में आप देख सकते हैं, निकाल सकते हैं, केवल लोअर केस 'टी' स्ट्रिंग के अंत करने के लिए, ले जाता है, जबकि नए स्ट्रिंग के अंत के लिए एक नया इटरेटर लौटने (नई स्ट्रिंग वर्ष स्ट्रिंग निर्भर है जहां हटा तत्व डाला जाता है करने के लिए) इस कारण है कि जब आप इटरेटर है कि आप से मिल गया प्रिंट "निकालें"

"Text with some spaces" 
    ^ ^removes both 't', then shift all elements forward -1 //what we want to remove 
    "Text with some spaces" 
         ^end of string     -2 //original state of string 
    "Tex with some spacess" 
          ^end of string      -3 //first 't' removed 
    "Tex wih some spaceses" 
          ^end of string      -4 //second 't' removed 
    "Tex wih some spaceses" 
         ^new iterator that remove() returned -5 // the state of string after "remove" and without "erase" 

अगर आप इटरेटर आप के लिए 5 कदम से प्राप्त पारित है "मिटा()" यह पता चल जाएगा प्रक्रिया में स्ट्रिंग को फिर से आकार देने के स्ट्रिंग के अंत तक वहां से मिटाने के लिए

5

क्या std :: निकाल क्या करता है?

यहां std::remove का छद्म कोड है। यह देखने के लिए कुछ सेकंड लें कि इसके क्या कर रहे हैं और फिर स्पष्टीकरण पढ़ें।

Iter remove(Iter start, Iter end, T val) { 
    Iter destination = start; 

    //loop through entire list 
    while(start != end) { 
     //skip element(s) to be removed 
     if (*start == val) { 
      start++; 
     } 
     else //retain rest of the elements 
      *destination++ = *start++; 
    } 

    //return the new end of the list 
    return destination; 
} 

सूचना है कि हटाने बस अनुक्रम में तत्वों ऊपर ले जाया गया, मूल्यों, जिसे आप निकालना चाहते थे अधिलेखित। तो जो मूल्य आप निकालना चाहते थे वे वास्तव में चले गए हैं, लेकिन फिर समस्या क्या है? मान लें कि आपके पास मूल्य {1, 2, 3, 4, 5} के साथ वेक्टर था। वैल = 3 के लिए निकालने के बाद, वेक्टर में अब {1, 2, 4, 5, 5} है। यही कारण है कि 4 और 5 ऊपर ले जाया गया ताकि 3 वेक्टर लेकिन वेक्टर के आकार नहीं बदला है से चला गया है, है। इसके अलावा, वेक्टर के अंत अब 5.

की प्रति के साथ इसमें अतिरिक्त बाईं वेक्टर :: मिटा क्या करता है शामिल हैं?

std::erase उस श्रेणी की शुरुआत और समाप्ति लेता है जिसे आप छुटकारा पाने के लिए चाहते हैं।यह मान नहीं लेता है, जिसे आप निकालना चाहते हैं, केवल श्रेणी के प्रारंभ और समाप्ति को हटाना चाहते हैं। यह ऐसे काम करता लिए छद्म कोड है:

erase(Iter first, Iter last) 
{ 
    //copy remaining elements from last 
    while (last != end()) 
     *first++ = *last++; 

    //truncate vector 
    resize(first - begin()); 
} 

तो मिटा आपरेशन वास्तव में कंटेनर के आकार बदलता है और इसलिए स्मृति मिल जाता है।

निकालें-मिटा मुहावरा

std::remove और std::erase के संयोजन आप इतना है कि कंटेनर वास्तव में काट होगा अगर तत्वों को हटा दिया गया कंटेनर से मिलान तत्वों को दूर करने के लिए अनुमति देता है। यहां बताया गया है कि यह कैसे करें:

//first do the remove 
auto removed = std::remove(vec.begin(), vec.end(), val); 

//now truncate the vector 
vec.erase(removed, vec.end()); 

इसे हटाने-मिटाए गए मुहावरे के रूप में जाना जाता है। यह इस तरह क्यों डिजाइन किया गया है? अंतर्दृष्टि यह है कि तत्वों को खोजने का संचालन अधिक सामान्य और अंतर्निहित कंटेनर से स्वतंत्र है (केवल इटरेटर पर निर्भर)। हालांकि मिटाने का संचालन इस बात पर निर्भर करता है कि कैसे कंटेनर मेमोरी संग्रहीत कर रहा है (उदाहरण के लिए, हो सकता है कि आपने गतिशील सरणी के बजाय लिंक लिंक्ड हो)। इसलिए एसटीएल जेनेरिक "हटाने" ऑपरेशन प्रदान करते समय कंटेनरों को अपना खुद का मिटाने की अपेक्षा करता है ताकि सभी कंटेनरों को उस कोड को लागू करने की आवश्यकता न हो। मेरे विचार में, नाम बहुत भ्रामक है और std::remove को std::find_move कहा जाना चाहिए था।

नोट: ऊपर कोड सख्ती से स्यूडोकोड है। वास्तविक एसटीएल कार्यान्वयन अधिक स्मार्ट है, उदाहरण के लिए, प्रतिलिपि के बजाय std::move का उपयोग करना।

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

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