2009-12-16 19 views
9

मुझे पता है कि जब delete [] सभी सरणी तत्वों के लिए विनाश का कारण बनता है और फिर स्मृति को जारी करता है।क्यों [] का उपयोग गतिशील रूप से आवंटित सरणी को हटाने के लिए हटाएं (हटाएं [])?

मैं शुरू में सोचा था कि यह सिर्फ संकलक सरणी में सभी तत्वों के लिए नाशक कॉल करने के लिए चाहता है, लेकिन मैं भी एक काउंटर है - कि जो है के लिए तर्क:

ढेर स्मृति allocator आबंटित बाइट्स के आकार का पता और चाहिए संसाधनों की रिसाव को रोकने के लिए sizeof(Type) का उपयोग करके तत्वों में से कोई भी नहीं ढूंढना और किसी सरणी के लिए विनाशकों को उपयुक्त कॉल करना संभव है।

तो मेरी धारणा सही है या नहीं और कृपया इस पर मेरा संदेह स्पष्ट करें।

तो मुझे delete [] में [] का उपयोग नहीं मिल रहा है?

उत्तर

34

स्कॉट मेयर्स अपने प्रभावी सी ++ पुस्तक में कहता है: आइटम 5: नए फॉर्म को हटाने और हटाने के समान फॉर्म का उपयोग करें।

हटाने के लिए बड़ा सवाल यह है: स्मृति में कितनी वस्तुएं रहती हैं? इसका उत्तर यह निर्धारित करता है कि कितने विनाशकों को बुलाया जाना चाहिए।

क्या पॉइंटर हटाया जा रहा है एक ऑब्जेक्ट या ऑब्जेक्ट्स की सरणी पर इंगित करता है? जानने के लिए एकमात्र तरीका यह है कि आप इसे बताने के लिए हैं। यदि आप अपने उपयोग के उपयोग में ब्रैकेट का उपयोग नहीं करते हैं, तो हटाएं कि एक ऑब्जेक्ट की ओर इशारा किया गया है।

इसके अलावा, स्मृति allocator और अधिक स्थान है कि अपने वस्तुओं की दुकान है और इस मामले स्मृति प्रत्येक वस्तु के काम नहीं करेगा के आकार से लौटे ब्लॉक के आकार को विभाजित में करने के लिए आवश्यक आवंटन हो सकता है।

मंच पर निर्भर करता है

, _msize (विंडोज़), malloc_usable_size (लिनक्स) या malloc_size (OSX) कार्यों आप ब्लॉक कि आवंटित किया गया था की वास्तविक लंबाई बता देंगे। बढ़ते कंटेनरों को डिजाइन करते समय इस जानकारी का शोषण किया जा सकता है।

एक और कारण यह काम नहीं करेगा कि Foo* foo = new Foo[10] मेमोरी आवंटित करने के लिए operator new[] पर कॉल करता है। फिर स्मृति को हटाने के लिए delete [] foo;operator delete[] पर कॉल करें। चूंकि उन ऑपरेटरों को अधिभारित किया जा सकता है, आपको सम्मेलन का पालन करना होगा अन्यथा delete foo; कॉल operator delete जो operator delete [] के साथ एक असंगत कार्यान्वयन हो सकता है। यह अर्थशास्त्र की बात है, न कि आवंटित ऑब्जेक्ट की संख्या को ट्रैक करने के बाद, बाद में विनाशक कॉल की सही संख्या जारी करने के लिए।

यह भी देखें:

[16.14] After p = new Fred[n], how does the compiler know there are n objects to be destructed during delete[] p?

लघु जवाब: जादू।

लंबा उत्तर: रन-टाइम सिस्टम ऑब्जेक्ट्स की संख्या संग्रहीत करता है, एन, कहीं कहीं इसे पुनर्प्राप्त किया जा सकता है यदि आप केवल पॉइंटर को जानते हैं, पी। ऐसा दो लोकप्रिय तकनीकें हैं जो ऐसा करती हैं। इन दोनों तकनीकों का वाणिज्यिक-ग्रेड कंपाइलर्स द्वारा उपयोग किया जाता है, दोनों में ट्रेडऑफ हैं, और न ही सही है।ये तकनीकें हैं:


संपादित करें: पढ़ा @AndreyT टिप्पणी करने के बाद, मैं Stroustrup की "डिजाइन और विकास सी के ++" की मेरी कॉपी में खोदा और निम्नलिखित कुछ अंश:

हम कैसे सुनिश्चित करते हैं कि एक सरणी सही ढंग से हटा दिया गया है? विशेष रूप से, हम कैसे सुनिश्चित करते हैं कि विनाशक को सरणी के सभी तत्वों के लिए बुलाया जाता है?

...

सादा हटाना दोनों व्यक्ति को संभालने के लिए आवश्यक नहीं है एक सरणियों वस्तुओं। यह व्यक्तिगत वस्तुओं को आवंटित करने और हटाने के सामान्य मामले को जटिल बनाने से बचाता है। यह सरणी deallocation के लिए आवश्यक जानकारी के साथ व्यक्तिगत वस्तुओं को इकट्ठा करने से बचाता है।

हटाए जाने के एक मध्यवर्ती संस्करण [] को प्रोग्रामर को सरणी के तत्वों की संख्या निर्दिष्ट करने की आवश्यकता होती है।

...

वह बहुत त्रुटियों की संभावना को साबित कर दिया है, इसलिए तत्वों की संख्या का ट्रैक रखने के बोझ के बजाय कार्यान्वयन पर रखा गया था।

जैसा कि @ मार्कस ने उल्लेख किया है, तर्कसंगत हो सकता है कि "आप जो भी उपयोग नहीं करते हैं उसके लिए आप भुगतान नहीं करते हैं"।


EDIT2:

में "सी ++ प्रोग्रामिंग भाषा, 3 संस्करण", §10.4.7, Bjarne Stroustrup लिखते हैं:

वास्तव में कैसे सरणियों और अलग-अलग वस्तुओं आवंटित किए जाते हैं कार्यान्वयन है निर्भर। इसलिए, विभिन्न कार्यान्वयन हटाए गए गलत उपयोगों और [] ऑपरेटरों को हटाने के लिए अलग-अलग प्रतिक्रिया देंगे। पिछले एक जैसे सरल और अनिच्छुक मामलों में, एक कंपाइलर समस्या का पता लगा सकता है, लेकिन आम तौर पर रन टाइम पर कुछ बुरा होगा।

सरणी के लिए विशेष विनाश ऑपरेटर, हटाएं [], तर्कसंगत रूप से आवश्यक नहीं है। हालांकि, मान लें कि मुक्त स्टोर के कार्यान्वयन के लिए प्रत्येक ऑब्जेक्ट के लिए पर्याप्त जानकारी रखने की आवश्यकता थी ताकि यह बताने के लिए कि यह एक व्यक्ति या सरणी है या नहीं। उपयोगकर्ता को बोझ से मुक्त किया जा सकता था, लेकिन उस दायित्व ने कुछ सी ++ कार्यान्वयन पर महत्वपूर्ण समय और अंतरिक्ष ओवरहेड लगाया होगा।

+0

स्कॉट मेयर्स पुस्तक के लिए +1: _all_ C++ प्रोग्रामर को प्रभावी सी ++ और अधिक प्रभावी सी ++ पुस्तकों का स्वामित्व और पढ़ना चाहिए। – AAT

+2

स्कॉट की * स्पष्टीकरण * वास्तव में बिल्कुल एक स्पष्टीकरण नहीं है। यह तथ्य का केवल एक अनुमान है, चालाकी से "स्पष्टीकरण" के रूप में पारित किया गया। वह कहता है कि यह जानने का एकमात्र तरीका है कि यह एक सरणी या एक वस्तु है, उपयोगकर्ता से पूछना है। यह निश्चित रूप से गलत है। उस जानकारी को 'नए []' में संग्रहीत करना पूरी तरह से संभव है और फिर 'डिलीट' में पुनर्प्राप्त करें, जैसा कि अब तत्व गणना के साथ किया गया है। निर्णय इसके खिलाफ किया गया था, क्योंकि यह अत्यधिक "जटिल" सूचना संरचना और 'हटाए' में शाखाबद्ध होगा। कोई "प्यारा" स्पष्टीकरण नहीं है, यह बस * तय * था। – AnT

+1

@ जेसन: नहीं, स्कॉट की व्याख्या पूरी तरह से फर्जी है। फिर, आप कैसे जानते हैं कि जब आप 'हटाएं'] करते हैं तो सरणी में कितने तत्व हैं? है ना? क्या आपको इसे 'हटाएं []' करने के लिए कहना है? नहीं। हटाएं [] '" जानता है "क्योंकि यह 'नई []' द्वारा तैयार की गई घरेलू जानकारी का उपयोग करता है। वैसे ही हम "सरणी या नहीं" प्रकृति की अतिरिक्त घरेलू जानकारी को स्टोर करने के लिए 'new' \' new [] 'को मजबूर कर सकते हैं। यह आसान और स्पष्ट है। यह आपके प्रश्न का उत्तर है। – AnT

3

ढेर स्वयं आवंटित ब्लॉक के आकार को जानता है - आपको केवल पते की आवश्यकता है। मुफ्त() कार्यों की तरह दिखें - आप केवल पते को पास करते हैं और यह स्मृति को मुक्त करता है।

delete (delete[]) और free() के बीच अंतर यह है कि पूर्व दो पहली कॉल विनाशकर्ता, तो मुक्त स्मृति (संभवतः free() का उपयोग) है। समस्या यह है कि delete[] में केवल एक ही तर्क है - पता और केवल उस पते को होने पर उसे विनाशकों को चलाने के लिए वस्तुओं की संख्या जानने की आवश्यकता है।तो new[] तत्वों की संख्या कहीं लिखने के कार्यान्वयन-परिभाषित तरीके का उपयोग करता है - आम तौर पर यह तत्वों की संख्या के साथ सरणी को प्रीपेड करता है। अब हटाएं [] उस कार्यान्वयन-विशिष्ट डेटा पर विनाशकों को चलाने के लिए और फिर मुफ्त मेमोरी (फिर से, केवल ब्लॉक पते का उपयोग करके) पर निर्भर करेगा।

+0

संकलक भी आकार पता नहीं करना चाहिए उत्सुक हूँ आवंटित वस्तुओं (संभावित पैडिंग सहित)। इस तरह, आवंटित स्मृति ब्लॉक के कुल आकार को देखते हुए, रनटाइम यह निर्धारित करने में सक्षम होना चाहिए कि यह किसी सरणी में एकल या कई ऑब्जेक्ट्स को हटा रहा है या नहीं। – Giorgio

+0

@Giorgio: ठेठ कार्यान्वयन में 'नई []' तत्वों की संख्या के साथ सरणी पहले जोड़ता और इतना है कि यह सरणी, नहीं ब्लॉक की शुरुआत की 0 तत्व पर अंक सूचक ऑफसेट। जब आप 'हटाएं' कहते हैं तो आपके पास एक पता है जो ऑफसेट है और आपके पास इसका कोई सार्वभौमिक साधन नहीं है। – sharptooth

-1

यह अधिक जटिल है।

क्रियान्वयन की सुविधा के लिए एक सरणी को हटाने के लिए इसका उपयोग करने के लिए कीवर्ड और सम्मेलन का आविष्कार किया गया था, और कुछ कार्यान्वयन इसका उपयोग करते हैं (मुझे नहीं पता कि एमएस वीसी ++ नहीं है)।

अन्य सभी मामलों में, तुम्हें पता है सटीक आकार अन्य तरीकों से मुक्त हो रहे हैं:

सुविधा यह है। जब आप एक ऑब्जेक्ट को हटाते हैं, तो आपके पास संकलन-समय आकार() से आकार हो सकता है। जब आप बेस पॉइंटर द्वारा पॉलिमॉर्फिक ऑब्जेक्ट को हटाते हैं और आपके पास वर्चुअल विनाशक होता है, तो आप आकार को vtbl में एक अलग प्रविष्टि के रूप में प्राप्त कर सकते हैं। यदि आप एक सरणी हटाते हैं, तो आप इसे मुक्त करने के लिए स्मृति के आकार को कैसे जानेंगे, जब तक कि आप इसे अलग से ट्रैक न करें? उदाहरण के तौर पर पता है कि उपयोगकर्ता के लिए दिया जाता है इससे पहले कि यह डालकर -

विशेष सिंटेक्स केवल एक सरणी के लिए इस तरह के आकार पर नज़र रखने की अनुमति होगी। यह अतिरिक्त संसाधन लेता है और गैर-सरणी के लिए इसकी आवश्यकता नहीं होती है।

+0

यह उन तत्वों की संख्या को ट्रैक करने के बारे में है जिन्हें उनके विनाशकों की आवश्यकता होती है, आवंटित स्मृति की मात्रा नहीं। –

1

delete[] बस एक अलग कार्यान्वयन (फ़ंक्शन) कहता है;

कोई कारण नहीं है कि आवंटक ट्रैक नहीं कर सका (वास्तव में, यह स्वयं लिखना काफी आसान होगा)।

मैं, कारण वे इसे प्रबंधन नहीं किया, या कार्यान्वयन के इतिहास पता नहीं है, तो मुझे लगता है कि करने के लिए किए गए: में से कई इन 'ठीक है, क्यों नहीं इस थोड़ा आसान था?' सी

  • प्रदर्शन
  • इस मामले में, प्रदर्शन के साथ

    1. अनुकूलता: (सी ++ में) प्रश्न से एक या अधिक करने के लिए नीचे आया था। delete बनाम delete[] का उपयोग करना काफी आसान है, मुझे विश्वास है कि इसे प्रोग्रामर से समझा जा सकता है और उचित रूप से तेज़ (सामान्य उपयोग के लिए) हो सकता है। delete[] केवल केवल कुछ अतिरिक्त फ़ंक्शन कॉल और संचालन (नाशक कॉल को छोड़ते हुए) की आवश्यकता है, लेकिन यह नष्ट करने के लिए प्रति कॉल है, और अनावश्यक क्योंकि प्रोग्रामर आम तौर पर प्रकार वह/वह के साथ काम कर रहा है जानता है (यदि नहीं, वहाँ की संभावना एक बड़ा है हाथ में समस्या)। तो यह सिर्फ आवंटक के माध्यम से फोन करने से बचाता है। इसके अतिरिक्त, आवंटन द्वारा इन एकल आवंटन को अधिक विस्तार से ट्रैक करने की आवश्यकता नहीं हो सकती है; सरणी के रूप में प्रत्येक आवंटन का इलाज करने के लिए छोटी आवंटन के लिए गिनती के लिए अतिरिक्त प्रविष्टियों की आवश्यकता होगी, इसलिए यह सरल आवंटन कार्यान्वयन सरलीकरण के कई स्तर हैं जो वास्तव में बहुत से लोगों के लिए महत्वपूर्ण हैं, क्योंकि यह बहुत कम स्तर का डोमेन है।

    9

    मुख्य कारण यह है कि अलग delete और delete[] रखने का निर्णय लिया गया था कि ये दो इकाइयां उतनी ही समान नहीं हैं जितनी पहली नजर में लग सकती है। एक बेवकूफ पर्यवेक्षक के लिए वे लगभग समान प्रतीत होते हैं: प्रक्रियाओं की संभावित संख्या में केवल अंतर के साथ ही नष्ट करें और हटा दें। हकीकत में, अंतर अधिक महत्वपूर्ण है।

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

    अजीब कुछ अन्य उत्तर में किए गए दावों के विपरीत, यह है, ज़ाहिर है, पूरी तरह से संभव delete और delete[] बस एक ही निर्माण है कि बहुत ही प्रारंभिक चरण पर इसकी शाखाएं हैं साथ बदलने के लिए, यानी यह के प्रकार का निर्धारण होगा मेमोरी ब्लॉक (सरणी या नहीं) घरेलू जानकारी का उपयोग करके new/new[] द्वारा संग्रहीत किया जाएगा, और उसके बाद delete या delete[] के समतुल्य उपयुक्त कार्यक्षमता पर जाएं। हालांकि, यह एक खराब डिजाइन निर्णय होगा, क्योंकि, एक बार फिर, दोनों की कार्यक्षमता बहुत अलग है। एक ही निर्माण में दोनों को मजबूर करना एक डीलोकेशन फ़ंक्शन के स्विस आर्मी चाकू बनाने जैसा होगा। साथ ही, एक गैर-सरणी से एक सरणी बताने में सक्षम होने के लिए हमें सादे new के साथ किए गए एकल-ऑब्जेक्ट मेमोरी आवंटन में भी घरेलू जानकारी का एक अतिरिक्त टुकड़ा पेश करना होगा। यह आसानी से एकल ऑब्जेक्ट आवंटन में उल्लेखनीय स्मृति ओवरहेड में परिणाम हो सकता है।

    लेकिन, एक बार फिर, मुख्य कारण यहां delete और delete[] के बीच कार्यात्मक अंतर है। इन भाषा इकाइयों में केवल स्पष्ट त्वचा-गहरी समानता होती है जो केवल निष्पक्ष विनिर्देश ("विनाश और मुक्त स्मृति") के स्तर पर मौजूद होती है, लेकिन एक बार जब कोई यह समझ में आता है कि इन संस्थाओं को वास्तव में क्या करना है, तो उन्हें यह पता चलता है कि वे बहुत अलग हैं एक में विलय करने के लिए।

    पीएस बीटीडब्लू इस सवाल में sizeof(type) के सुझाव के साथ समस्याओं में से एक है। delete की संभावित पॉलीमोर्फिक प्रकृति की वजह से, आपको delete में टाइप नहीं पता है, इसलिए आप sizeof(type) प्राप्त नहीं कर सकते हैं। इस विचार के साथ और अधिक समस्याएं हैं, लेकिन यह स्पष्ट करने के लिए पहले से ही पर्याप्त है कि यह क्यों नहीं उड़ जाएगा।

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