2008-11-20 22 views
47

सी ++ का सबसे छोटा हिस्सा क्या है जो आप वेक्टर या पॉइंटर्स की सूची को सुरक्षित रूप से साफ करने के लिए आ सकते हैं? (यह मानते हुए आप कॉल करने के लिए संकेत पर हटाना चाहते हैं?)पॉइंटर्स की एक एसटीएल सूची/वेक्टर की सफाई

list<Foo*> foo_list; 

मैं नहीं बल्कि बूस्ट का उपयोग या स्मार्ट संकेत के साथ अपने संकेत रैप नहीं चाहते हैं।

+2

स्मार्ट संकेत (बूस्ट :: shared_ptr सहित) परिस्थितियों में अपने वस्तुओं को नष्ट आप मुश्किल के एक महान सौदा देखकर कि यह मैन्युअल रूप से किया है होगा जहां होगा। –

+2

अपने पॉइंटर्स को हटाने के लिए कंटेनर के बाहर कोड पर भरोसा करना वास्तव में खतरनाक है। क्या होता है जब एक अपवाद के कारण कंटेनर नष्ट हो जाता है, उदाहरण के लिए? मुझे पता है कि आपने कहा कि आपको बढ़ावा पसंद नहीं है, लेकिन कृपया [बूस्ट पॉइंटर कंटेनर] (http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/ptr_container.html) पर विचार करें। –

+0

i second you राय –

उत्तर

53

के बाद से हम यहाँ चुनौती नीचे फेंक रहे हैं ... "सी की सबसे छोटा हिस्सा ++"

static bool deleteAll(Foo * theElement) { delete theElement; return true; } 

foo_list . remove_if (deleteAll); 

मुझे लगता है कि हम लोग कुशल एल्गोरिदम के लिए एसटीएल के साथ आया था भरोसा कर सकते हैं। पहिया को फिर से क्यों शुरू करें?

+0

में ऐसी सूची में हेरफेर के रूप में अधिक जटिल है। मैंने इसके बजाय एक फ़ंक्शन टेम्पलेट लिखा था, लेकिन remove_if विचार अच्छा है। – twk

+21

उद्देश्य प्राप्त करता है लेकिन बहुत हैकी लगता है ... –

+29

क्या यह सिर्फ मुझे है या क्या वास्तव में गलत लगता है जब एक भविष्य के दुष्प्रभाव होते हैं? –

8
template< typename T > 
struct delete_ptr : public std::unary_function<T,bool> 
{ 
    bool operator()(T*pT) const { delete pT; return true; } 
}; 

std::for_each(foo_list.begin(), foo_list.end(), delete_ptr<Foo>()); 
28
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); ++it) 
{ 
    delete *it; 
} 
foo_list.clear(); 
+0

उपरोक्त है क्योंकि यह अच्छा प्रदर्शन करता है और छोटा है। मैंने आपके उत्तर का एक संशोधित संस्करण जोड़ा जो काफी छोटा है (लेकिन सी ++ 11 पर निर्भर करता है)। – Adisak

+0

प्रश्न ने कॉन्स्ट सूची निर्दिष्ट नहीं की थी। कॉन्स इटरेटर का उपयोग क्यों करें? – SRINI794

+1

@ SRINI794 मैंने कोड की प्रयोज्यता के लिए अधिकतम करने के लिए एक कॉन्स इटरेटर का उपयोग किया, और क्योंकि मैं पुनरावृत्ति के दौरान सूची को बदलने वाला नहीं था, इसलिए मुझे एक परिवर्तनीय पुनरावर्तक की आवश्यकता नहीं थी। –

6

मुझे यकीन है कि functor दृष्टिकोण यहाँ संक्षिप्तता के लिए जीत नहीं हूँ।

for(list<Foo*>::iterator i = foo_list.begin(); i != foo_list.end(); ++i) 
    delete *i; 

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

+0

मज़ेदार दृष्टिकोण ब्रेवटी के लिए जीत नहीं सकता है, लेकिन यह एक पुरस्कार का अधिक नहीं है। एक मज़ेदार का उपयोग करके आप लूप के लिए अपना खुद का लिखने से बचें, जो सॉफ़्टवेयर में कई दोषों का स्रोत है। –

+0

प्रश्न विशेष रूप से "सबसे छोटा" के बारे में पूछा गया। मुझे नहीं पता था कि लूप के लिए मैनुअल दोषों का एक प्रमुख स्रोत थे, क्या आप एक संदर्भ प्रदान कर सकते हैं? अगर मेरी टीम के एक सदस्य को लूप के लिए एक बग फ्री लिखने में समस्या थी, तो मैं उसे एक मजेदार समाधान पर ढीला नहीं होने देना चाहता था। –

+0

अनुरोध कंटेनर को "सुरक्षित रूप से साफ" करना था। लेकिन उस कोड की सुरक्षा अपवादों को फेंकने के कई तरीकों पर निर्भर करती है: 'प्रारंभ() ',' अंत()', 'iterator :: ऑपरेटर!= ',' iterator :: ऑपरेटर * ',' iterator :: ऑपरेटर ++ '। आश्चर्यजनक रूप से, इस तरह की निर्भरता असुरक्षित है: http://stackoverflow.com/questions/7902452/may-stl-iterator-methods-throw-an-exception – Raedwald

0
for (list<Foo*>::const_iterator i = foo_list.begin(), e = foo_list.end(); i != e; ++i) 
    delete *i; 
foo_list.clear(); 
51

std::list<T*> उपयोग के लिए:

while(!foo.empty()) delete foo.front(), foo.pop_front(); 

std::vector<T*> उपयोग के लिए:

while(!bar.empty()) delete bar.back(), bar.pop_back(); 

सुनिश्चित नहीं हैं कि मैं क्यों ऊपर std::list के लिए front बजाय back ले लिया। मुझे लगता है कि यह महसूस कर रहा है कि यह तेज़ है। लेकिन वास्तव में दोनों स्थिर समय हैं :)। वैसे भी एक समारोह में लपेट और मजा:

template<typename Container> 
void delete_them(Container& c) { while(!c.empty()) delete c.back(), c.pop_back(); } 
+2

तकनीकी रूप से सही है, लेकिन यदि आप अधिक सामान्य ब्रेस और इंडेंटिंग का उपयोग करते हैं तो यह बहुत लंबा हो जाता है सम्मेलनों। –

+0

इसे बाएं से दाएं से पढ़ें: जबकि foo खाली नहीं है, foo के सामने हटाएं, और foo के सामने पॉप करें: p newlines केवल रास्ते में आ जाएंगे:/ –

+5

मैं अल्पविराम (अनुक्रम) ऑपरेटर का उपयोग करने के खिलाफ अनुशंसा करता हूं । बहुत सारे सी ++ देवों को पता नहीं है कि यह क्या करता है, और इसे अर्धविराम के लिए गलती होगी। –

13

यह वास्तव में कंटेनर के बाहर कोड पर भरोसा करने के लिए अपने संकेत से हटाने के लिए खतरनाक है। क्या होता है जब एक अपवाद के कारण कंटेनर नष्ट हो जाता है, उदाहरण के लिए?

मुझे पता है कि आपने कहा था कि आपको बढ़ावा पसंद नहीं है, लेकिन कृपया boost pointer containers पर विचार करें।

+0

i सेकंड आप राय –

+0

नुकसान में से एक यह है कि, विपरीत रूप से, एसटीएल अपवादों को फेंकने के लिए कई महत्वपूर्ण इटरेटर ऑपरेशन की अनुमति देता है। यह एक कंटेनर असुरक्षित के माध्यम से पुनरावृत्ति का उपयोग कर कई "स्पष्ट" दृष्टिकोण बनाता है। Http://stackoverflow.com/questions/7902452/may-stl-iterator-methods-throw-an-exception – Raedwald

+0

@YogeshArora मुझे भी देखें, लेकिन यह किसी भी तरह से यह एक वैध उत्तर नहीं देता है। स्थिरता वर्कअराउंड की वजह से –

4

कम से कम एक सूची के लिए, पुनरावृत्त और हटाना, फिर अंत में स्पष्ट कॉल करना थोड़ा अनावश्यक है क्योंकि इसमें सूची को दो बार घुमाने में शामिल है, जब आपको वास्तव में केवल एक बार ऐसा करना होता है। यहाँ थोड़ा बेहतर तरीका है:

for (list<Foo*>::iterator i = foo_list.begin(), e = foo_list.end(); i != e;) 
{ 
    list<Foo*>::iterator tmp(i++); 
    delete *tmp; 
    foo_list.erase(tmp); 
} 

जिसके अनुसार, अपने संकलक स्मार्ट कैसे सूची :: स्पष्ट कार्यान्वित किया जाता है के आधार पर दो वैसे भी गठबंधन पाश करने के लिए पर्याप्त हो सकता है।

+0

+1। मुझे लगता है कि सी ++ की एक बड़ी समस्या यह है कि यह कोड पहले से ही पुराने पुराने सी – peterh

3

दरअसल, मेरा मानना ​​है कि एसटीडी पुस्तकालय allocator class

के रूप में स्मृति प्रबंध आप स्वचालित रूप से किसी भी कंटेनर के सदस्यों को हटाने के लिए बुनियादी संभाजक के पुनःआवंटन() विधि विस्तार कर सकते हैं का एक सीधा तरीका प्रदान करता है।

मैं/सोचता हूं/यह उस चीज का प्रकार है जिसका उद्देश्य यह है।

5

निम्नलिखित हैक पॉइंटर्स को हटा देता है जब आपकी सूची RAII का उपयोग करके दायरे से बाहर हो जाती है या यदि आप सूची :: clear() कहते हैं।

template <typename T> 
class Deleter { 
public: 
    Deleter(T* pointer) : pointer_(pointer) { } 
    Deleter(const Deleter& deleter) { 
    Deleter* d = const_cast<Deleter*>(&deleter); 
    pointer_ = d->pointer_; 
    d->pointer_ = 0; 
    } 
    ~Deleter() { delete pointer_; } 
    T* pointer_; 
}; 

उदाहरण:

std::list<Deleter<Foo> > foo_list; 
foo_list.push_back(new Foo()); 
foo_list.clear(); 
+0

मुझे यह पसंद है! यह shared_ptr की तरह काम करता है लेकिन केवल कंटेनर के लिए और यह बहुत ही कम और सुरुचिपूर्ण है। मैं दूसरे दिन बूस्ट स्मार्ट पॉइंटर्स को देख रहा था और यह 200K से अधिक है - समझ में नहीं आता - स्रोत कोड! – Dimitris

+0

यह वास्तव में साफ है! यदि आप पॉइंटर अव्यवस्था (*) और सदस्य चयन (->) ऑपरेटर को अधिभारित करते हैं तो आप इसे पूरी तरह पारदर्शी बना सकते हैं, मैंने सोचा होगा। – jsdw

1
void remove(Foo* foo) { delete foo; } 
.... 
for_each(foo_list.begin(), foo_list.end(), remove); 
+0

आप जानते हैं कि 'foo' हटाएं पहले से ही जांचता है कि' foo' 'nullptr' है, है ना? –

+1

@ क्रिस्टियन राउ अब मैं करता हूं। धन्यवाद – kendotwill

15

आप सी ++ 11 के लिए अनुमति देते हैं, तो आप डगलस Leeder के जवाब का एक बहुत ही लघु संस्करण कर सकते हैं:

for(auto &it:foo_list) delete it; foo_list.clear(); 
+0

यह वास्तव में एक कारण है कि आप 2n बार लूपिंग कर रहे हैं। एक बार प्रत्येक तत्व के माध्यम से लूप करने और स्मृति को हटाने के बाद सूची को साफ़ करने के लिए सूची के माध्यम से फिर से लूपिंग और सूची को शून्य पर वापस लाएं। आप जो करना चाहते हैं वह ऊपर दिए गए श्री रे या जोहान्स शैब के उत्तर जैसे दृष्टिकोण का उपयोग करना है। दोनों सूची में एक बार फिर से स्मृति के हटाने और आकार आकार के एक लूप में सूची आकार को कम करने पर प्रदर्शन करते हैं। – Matthew

4
for(list<Foo*>::const_iterator it = foo_list.begin(); it != foo_list.end(); it++) 
{ 
    delete *it; 
} 
foo_list.clear(); 

नहीं है एक छोटा कारण यह है कि आप ऐसा क्यों नहीं करना चाहते हैं - आप प्रभावी ढंग से फिर से चल रहे हैं सूची दो बार।

std :: सूची <> :: स्पष्टता जटिलता में रैखिक है; यह एक लूप के भीतर एक समय में एक तत्व को हटा देता है और नष्ट कर देता है।

विचार सरलतम में ऊपर ले रहा है मेरी राय में समाधान को पढ़ने के लिए है:

while(!foo_list.empty()) 
{ 
    delete foo_list.front(); 
    foo_list.pop_front(); 
} 
3

के बाद से सी ++ 11:

std::vector<Type*> v; 
... 
std::for_each(v.begin(), v.end(), std::default_delete<Type>()); 

या, यदि आप टेम्प्लेटेड कोड लिखने और करना चाहते हैं एक ठोस प्रकार को निर्दिष्ट से बचने:

std::for_each(v.begin(), v.end(), 
    std::default_delete<std::remove_pointer<decltype(v)::value_type>::type>()); 

कौन सा (के बाद से सी ++ 14) के रूप में छोटा किया जा सकता:

std::for_each(v.begin(), v.end(), 
    std::default_delete<std::remove_pointer_t<decltype(v)::value_type>>()); 
संबंधित मुद्दे