2012-01-18 8 views
19

मैं कुछ पढ़ रहा हूं कि जब आप प्लेसमेंट नया का उपयोग करते हैं तो आपको विनाशक को मैन्युअल रूप से कॉल करना होगा। के रूप में मैं ऑपरेटर delete सामान्य रूप से नाशक कॉल और फिर स्मृति deallocates पता है, सहीप्लेसमेंट द्वारा आवंटित स्मृति को सही तरीके से कैसे मुक्त करें?

// Allocate memory ourself 
char* pMemory = new char[ sizeof(MyClass)]; 

// Construct the object ourself 
MyClass* pMyClass = new(pMemory) MyClass(); 

// The destruction of object is our duty. 
pMyClass->~MyClass(); 

जहां तक:

folowing कोड पर विचार करें? तो हम इसके बजाय delete का उपयोग क्यों नहीं करते?

delete pMyClass; //what's wrong with that? 
पहले मामले में

हम nullptr को pMyClass स्थापित करने के लिए हम इस तरह नाशक फोन के बाद मजबूर हैं:

pMyClass->~MyClass(); 
pMyClass = nullptr; // is that correct? 

लेकिन नाशक स्मृति पुनःआवंटन नहीं था, है ना? तो क्या यह एक स्मृति रिसाव होगा?

मैं उलझन में हूं, क्या आप इसे समझा सकते हैं?

+1

+1 अच्छा सवाल है, मुझे यह भी समझना अच्छा लगेगा। – AraK

+12

खैर, तकनीकी रूप से, नियुक्ति 'नया' स्मृति आवंटित नहीं करता है। लेकिन यह इस संदर्भ में एक विस्तार है। – Griwes

+1

@Griwes: +1 तर्कसंगत रूप से यह एक मौलिक बिंदु है :) –

उत्तर

32

new ऑपरेटर का उपयोग दो चीजें करता है, यह फ़ंक्शन operator new को कॉल करता है जो स्मृति आवंटित करता है, और फिर यह उस स्मृति में ऑब्जेक्ट बनाने के लिए प्लेसमेंट को नया उपयोग करता है। delete ऑपरेटर ऑब्जेक्ट के विनाशक को कॉल करता है, और फिर operator delete पर कॉल करता है। हाँ, नाम उलझन में हैं।

//normal version     calls these two functions 
MyClass* pMemory = new MyClass; void* pMemory = operator new(sizeof(MyClass)); 
            MyClass* pMyClass = new(pMemory) MyClass(); 
//normal version     calls these two functions 
delete pMemory;     pMyClass->~MyClass(); 
            operator delete(pMemory); 

अपने मामले में, आपने मैन्युअल रूप से प्लेसमेंट का उपयोग किया है, तो आपको विनाशक को मैन्युअल रूप से कॉल करने की भी आवश्यकता है। चूंकि आपने मैन्युअल रूप से मेमोरी आवंटित की है, इसलिए आपको इसे मैन्युअल रूप से रिलीज़ करने की आवश्यकता है। याद रखें, अगर आप new के साथ आवंटित करते हैं, तो delete को एक कॉपरपॉन्डिंग होना चाहिए। हमेशा।

हालांकि, नई नियुक्ति के आंतरिक बफ़र्स के साथ काम करने के साथ-साथ (और अन्य परिदृश्यों), जहां बफ़र्स थे बनाया गया हैoperator new साथ आवंटित है, जिसके कारण आप उन पर operator delete नहीं बुलाना चाहिए नहीं।

#include <type_traits> 

struct buffer_struct { 
    std::aligned_storage<sizeof(MyClass)>::type buffer; 
}; 
int main() { 
    buffer_struct a; 
    MyClass* pMyClass = new (&a.buffer) MyClass(); //created inside buffer_struct a 
    //stuff 
    pMyClass->~MyClass(); //can't use delete, because there's no `new`. 
    return 0; 
} 

buffer_struct वर्ग के उद्देश्य बनाने के लिए और नष्ट जो कुछ भी तरह से भंडारण, mainMyClass के निर्माण/विनाश का ख्याल रखता है, जबकि, ध्यान दें कि कैसे दो (लगभग *) एक दूसरे से पूरी तरह अलग है ।

* हम सुनिश्चित होना जरूरी भंडारण बड़ा पर्याप्त

+2

यह सवाल की जड़ है: "हालांकि, प्लेसमेंट नया आंतरिक बफर के लिए डिज़ाइन किया गया है, जिसे स्वयं नए के साथ आवंटित नहीं किया गया था, यही कारण है कि आपको उन पर डिलीट नहीं करना चाहिए।" आप स्मृति को मुक्त नहीं करते हैं क्योंकि आप भविष्य में अपने ही बफर के उस टुकड़े का उपयोग किसी अन्य प्रकार के किसी अन्य ऑब्जेक्ट के लिए करेंगे। आप विनाशक को बुलाते हैं ताकि उदाहरण साफ हो सके। –

+0

अच्छा उदाहरण, तो बफर को हटाने के लिए हम कॉल करते हैं: हटाएं [] a.buffer अगर यह द्विपक्षीय होगा, और फिर हम MyClass के विनाशक को कैसे कॉल करते हैं? – codekiddy

+0

@codekiddy: चूंकि 'a.buffer' गतिशील रूप से आवंटित नहीं किया गया है, इसलिए इसे अस्वीकार करने की आवश्यकता नहीं है। मेरे नमूने में यह स्वचालित रूप से आवंटित किया जाता है, इसलिए बफर हर तरह से पूरी तरह से स्वचालित है। एकमात्र चिंता कक्षा के नए प्लेसमेंट है, और फिर उस वर्ग के विनाशक को बुलाती है, क्योंकि मेरा नमूना कोड दिखाता है। –

9

एक कारण यह गलत है:

delete[] pMemory; 

आप उपरोक्त दोनों नहीं कर सकते:

delete pMyClass; 

कि आप delete[] साथ pMemory हटाना होगा, क्योंकि यह एक सरणी है।

इसी प्रकार, आप पूछ सकते हैं कि आप मेमोरी आवंटित करने के लिए malloc() का उपयोग क्यों नहीं कर सकते हैं, ऑब्जेक्ट बनाने के लिए नया प्लेसमेंट, और फिर delete मेमोरी को हटाने और मुक्त करने के लिए। इसका कारण यह है कि आपको malloc() और free() से मेल खाना चाहिए, malloc() और delete नहीं।

असली दुनिया में, प्लेसमेंट नई और स्पष्ट विनाशक कॉल का लगभग कभी उपयोग नहीं किया जाता है। इन्हें मानक लाइब्रेरी कार्यान्वयन (या टिप्पणियों में उल्लिखित अन्य सिस्टम-स्तरीय प्रोग्रामिंग के लिए आंतरिक रूप से उपयोग किया जा सकता है), लेकिन सामान्य प्रोग्रामर उनका उपयोग नहीं करते हैं। मैंने कभी भी सी ++ करने के कई वर्षों में उत्पादन कोड के लिए ऐसी चाल का उपयोग नहीं किया है।

+0

बस इसका उल्लेख करने के लिए: ओएस विकास या सी ++ में कुछ समान काम करते समय नियुक्ति 'नया' भी अक्सर उपयोग किया जाता है। – Griwes

+0

नमस्ते, इसलिए हमें विच्छेदन को नष्ट करने के बाद से विनाशक मैन्युअल को कॉल करने की आवश्यकता नहीं है [] pMemory विनाशक को बुलाएगा और स्मृति को मुक्त कर देगा? – codekiddy

+2

@codekiddy: 'हटाएं [] pMemory' * आपके ऑब्जेक्ट के लिए विनाशक को कॉल नहीं करता है। –

4

आप delete ऑपरेटर और operator delete के बीच अंतर करने की जरूरत हो गया है। विशेष रूप से, अगर आप प्लेसमेंट नए प्रयोग कर रहे हैं, आप स्पष्ट रूप से नाशक आह्वान और फिर स्मृति जारी करने के लिए operator delete (और नहीं delete ऑपरेटर) कहते हैं, यानी

X *x = static_cast<X*>(::operator new(sizeof(X))); 
new(x) X; 
x->~X(); 
::operator delete(x); 

ध्यान दें कि यह का उपयोग करता है operator delete है, जो निचले है delete ऑपरेटर से स्तर और विनाशकों के बारे में चिंता नहीं करता है (यह अनिवार्य रूप से free जैसा थोड़ा सा है)। इसकी तुलना delete ऑपरेटर से करें, जो आंतरिक रूप से विनाशक का आह्वान करने और operator delete पर कॉल करने के बराबर करता है।

यह ध्यान देने योग्य आप ::operator new और ::operator delete उपयोग करने के लिए आबंटित कर अपने बफर पुनःआवंटन की जरूरत नहीं है कि लायक है - जहाँ तक नियुक्ति नए संबंध है, यह कोई बात नहीं कैसे बफर जा रहा है/नष्ट कर दिया जाता है में आता है। मुख्य बिंदु स्मृति आवंटन और वस्तु जीवनकाल की चिंताओं को अलग करना है।

संयोग से, इसका एक संभावित अनुप्रयोग किसी गेम की तरह होगा, जहां आप अपने मेमोरी उपयोग को सावधानीपूर्वक प्रबंधित करने के लिए मेमोरी अप-फ्रंट का एक बड़ा ब्लॉक आवंटित करना चाहते हैं। फिर आप उस स्मृति में ऑब्जेक्ट्स का निर्माण करेंगे जो आपने पहले ही हासिल कर लिया है।

एक और संभावित उपयोग एक अनुकूलित छोटे, निश्चित आकार ऑब्जेक्ट आवंटक में होगा।

+0

+1 इसे इंगित करने के लिए धन्यवाद! – codekiddy

3

यह शायद समझने के लिए अगर आप एक स्मृति के ब्लॉक के भीतर कई MyClass वस्तुओं का निर्माण कल्पना आसान है।

उस मामले में, यह की तरह कुछ जाना होगा:

  1. नए चार का उपयोग कर स्मृति का एक विशाल ब्लॉक का आवंटन [10 * sizeof (MyClass)] या malloc (10 * sizeof (MyClass))
  2. उस स्मृति के भीतर दस MyClass ऑब्जेक्ट्स बनाने के लिए नया प्लेसमेंट का उपयोग करें।
  3. कुछ करो।
  4. अपनी प्रत्येक ऑब्जेक्ट के विनाशक को कॉल करें
  5. हटाएं [] या मुक्त() का उपयोग करके स्मृति के बड़े ब्लॉक को हटा दें।

इस बात की तरह अगर आप एक संकलक, या एक ओएस, आदि

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

यह भी ध्यान दें कि ग्रेग ने कहा है कि, इस मामले में, आप डिलीट का उपयोग नहीं कर सकते हैं, क्योंकि आपने सरणी को नए [] के साथ आवंटित किया है, इसलिए आपको हटाएं [] का उपयोग करना होगा।

यह भी ध्यान रखें कि आपको यह मानना ​​होगा कि आपने MyClass के लिए डिलीट नहीं किया है, अन्यथा यह पूरी तरह से कुछ अलग करेगा जो लगभग "नया" के साथ असंगत है।

तो मुझे लगता है कि आप वर्णन करते हुए "हटाएं" को कॉल करना चाहते हैं, लेकिन क्या यह कभी काम कर सकता है? मुझे लगता है कि यह मूल रूप से एक ही सवाल है "मेरे पास दो असंबंधित प्रकार हैं जिनके पास विनाशक नहीं हैं। क्या मैं एक प्रकार के सूचक को नया कर सकता हूं, फिर उस स्मृति को किसी अन्य प्रकार के सूचक से हटा दें?" दूसरे शब्दों में, "क्या मेरे कंपाइलर में सभी आवंटित सामानों की एक बड़ी सूची है, या यह विभिन्न प्रकार के लिए अलग-अलग चीजें कर सकती है"।

मुझे डर है कि मुझे यकीन नहीं है। कल्पना पढ़ना यह कहते हैं:

5.3.5 ... यदि [नष्ट ऑपरेटर की] संकार्य के स्थिर प्रकार की गतिशील प्रकार से अलग है, स्थिर प्रकार संकार्य के एक आधार वर्ग होगा गतिशील प्रकार और स्थिर प्रकार में वर्चुअल विनाशक होगा या व्यवहार अपरिभाषित है।

मुझे लगता है कि इसका मतलब है कि "यदि आप दो असंबंधित प्रकार उपयोग करते हैं, यह काम नहीं करता है (यह एक आभासी नाशक के माध्यम से polymorphically एक वर्ग वस्तु को हटाने के लिए ठीक है)।"

तो नहीं, ऐसा मत करो। मुझे संदेह है कि यह अक्सर अभ्यास में काम कर सकता है, अगर संकलक पूरी तरह से पते पर दिखता है और प्रकार नहीं (और न तो प्रकार एक बहु-विरासत वर्ग है, जो पते को उलझाएगा), लेकिन कोशिश न करें।

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