184

क्या इसे delete this; पर अनुमति दी गई है यदि हटाई गई कथन अंतिम विवरण है जो कक्षा के उस उदाहरण पर निष्पादित किया जाएगा? बेशक मुझे यकीन है कि this -Pointer द्वारा प्रतिनिधित्व किया गया ऑब्जेक्ट new ly-बनाया है।क्या यह अनुमति है?

void SomeModule::doStuff() 
{ 
    // in the controller, "this" object of SomeModule is the "current module" 
    // now, if I want to switch over to a new Module, eg: 

    controller->setWorkingModule(new OtherModule()); 

    // since the new "OtherModule" object will take the lead, 
    // I want to get rid of this "SomeModule" object: 

    delete this; 
} 

मैं यह कर सकता:

मैं कुछ इस तरह के बारे में सोच रहा हूँ?

+7

मुख्य समस्या यह होगी कि यदि आप इसे हटाते हैं तो आपके पास है उस वर्ग की वस्तुओं को बनाने के लिए कक्षा और आवंटन विधि के बीच एक तंग युग्मन बनाया। यह ओओ डिजाइन बहुत खराब है, क्योंकि ओओपी में सबसे मौलिक चीज स्वायत्त वर्ग बनाना है जो उनके कॉलर के बारे में नहीं जानते या उनकी परवाह नहीं करते हैं। इस प्रकार एक उचित ढंग से डिज़ाइन किया गया क्लास यह नहीं जानता कि उसे आवंटित किया गया था या नहीं। यदि आपको किसी कारण से ऐसी असाधारण तंत्र की आवश्यकता है, तो मुझे लगता है कि एक बेहतर डिजाइन वास्तविक वर्ग के चारों ओर एक रैपर वर्ग का उपयोग करना होगा, और रैपर को आवंटन के साथ सौदा करने दें। – Lundin

उत्तर

190

सी ++ पूछे जाने वाले प्रश्न Lite इस

के लिए विशेष रूप से एक प्रविष्टि है मैं इस बोली यह सार अच्छी तरह से

जब तक आप सावधान कर रहे हैं, यह है के रूप में लगता है एक वस्तु के लिए आत्महत्या करने के लिए ठीक है (इसे हटाएं)।

+11

संबंधित एफक्यूए में कुछ उपयोगी टिप्पणी भी है: http://yosefk.com/c++fqa/heap.html#fqa-16.15 –

+1

सुरक्षा के लिए आप मूल वस्तु पर निजी विनाशक का उपयोग यह सुनिश्चित करने के लिए कर सकते हैं कि इसका निर्माण नहीं किया गया है ढेर या एक सरणी या वेक्टर के हिस्से के रूप में। –

+0

'सावधान' परिभाषित करें – CinCout

66

परिणाम हाँ, delete this; परिभाषित किया गया है, जब तक कि (जैसा कि आपने ध्यान दिया) आपको विश्वास दिलाता हूं वस्तु गतिशील आवंटित किया गया था, और (बेशक) के बाद इसे नष्ट कर दिया है वस्तु उपयोग करने का प्रयास कभी नहीं।

कुछ लोग इसे एक बुरा हैक मानते हैं, और किसी को भी बताएं जो सुनेंगे कि इसे टालना चाहिए। एक सामान्य रूप से उद्धृत समस्या यह सुनिश्चित करने में कठिनाई होती है कि कक्षा की वस्तुओं को गतिशील रूप से आवंटित किया जाता है। अन्य इसे एक पूरी तरह से उचित मुहावरे मानते हैं, और हर समय इसका इस्तेमाल करते हैं। निजी तौर पर, मैं बीच में कहीं भी हूं: मैं शायद ही कभी इसका उपयोग करता हूं, लेकिन ऐसा करने में संकोच नहीं करते जब यह नौकरी के लिए सही उपकरण लगता है।

संपादित करें: [ज्यादातर @Alexandre सी की टिप्पणी के जवाब में]: प्राथमिक बार ऐसा करने पर एक वस्तु एक जीवन लगभग पूरी तरह से अपने आप ही है कि है कि के साथ है। एक उदाहरण जेम्स कन्ज़ ने उद्धृत किया है कि उसने एक फोन कंपनी के लिए एक बिलिंग/ट्रैकिंग प्रणाली बनाई थी। असल में, जब आप फोन उठाते हैं, तो कुछ उस पर ध्यान देता है और phone_call ऑब्जेक्ट बनाता है। उस समय आगे से, phone_call वस्तु (, एक कनेक्शन है जब आप डायल कर रही है डेटाबेस के लिए एक प्रवेश जब कॉल प्रारंभ किया कहने के लिए, संभवतः अधिक लोगों को कनेक्ट करता है, तो आप एक सम्मेलन बुलाने, आदि कर जोड़ने) फोन कॉल का ब्यौरा संभालती जब आप लटका, phone_call वस्तु अपने अंतिम पुस्तक कीपिंग (जैसे, डेटाबेस जब आप काट दिया कहने के लिए करने के लिए एक प्रविष्टि जोड़ देता है, तो वे गणना कर सकता है कितनी देर तक अपने फोन था) करता है और उसके बाद ही नष्ट कर देता है। phone_call वस्तु के जीवनकाल जब तुम्हें लेने पर आधारित है/फ़ोन रख - प्रणाली के बाकी हिस्सों के दृष्टिकोण से, यह मूल रूप से पूरी तरह से मनमाने ढंग से है, तो आप कर सकते हैं में किसी भी शाब्दिक गुंजाइश नहीं टाई यह कोड, या उस आदेश पर कुछ भी।

किसी भी व्यक्ति के लिए जो इस बात की परवाह कर सकता है कि इस तरह के कोडिंग कितने भरोसेमंद हो सकते हैं: यदि आप यूरोप के लगभग किसी भी हिस्से से फोन कॉल करते हैं, तो यह बहुत अच्छा मौका है कि इसे संभाला जा रहा है (कम से कम भाग) कोड द्वारा यह वास्तव में करता है।

+1

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

+0

@Alexandre: शायद आप ज्यादातर मामलों में ऐसा करेंगे, वैसे भी - मैं उस प्रणाली के सभी विवरणों के करीब कहीं भी नहीं जानता जो वह काम कर रहा था, इसलिए मैं इसके बारे में निश्चित रूप से नहीं कह सकता। –

+0

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

38

यह आपको डराता है, तो उसके लिए पूरी तरह से कानूनी हैक है:

void myclass::delete_me() 
{ 
    std::unique_ptr<myclass> bye_bye(this); 
} 

मुझे लगता है कि delete this मुहावरेदार सी ++ है, हालांकि, और मैं केवल एक जिज्ञासा के रूप में इस प्रस्तुत करते हैं।

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

void myclass::throw_error() 
{ 
    std::unique_ptr<myclass> bye_bye(this); 
    throw std::runtime_exception(this->error_msg); 
} 

नोट: यदि आप पुराने सी ++ 11 की तुलना में एक संकलक उपयोग कर रहे हैं आप उपयोग कर सकते std::auto_ptr बजाय std::unique_ptr, यह एक ही बात करेंगे।

+5

+1 क्योंकि मैं अभी हिंसक रूप से हँसे: डी –

+0

मैं इसे C++ 11 का उपयोग करके संकलित करने के लिए नहीं प्राप्त कर सकता, क्या इसके लिए कुछ विशेष कंपाइलर विकल्प हैं? इसके अलावा इस सूचक के कदम की आवश्यकता नहीं है? – Owl

+0

@ ओउएल सुनिश्चित नहीं है कि आपका क्या मतलब है, यह मेरे लिए काम करता है: http://ideone.com/aavQUK। * अन्य * 'unique_ptr' से' unique_ptr' बनाना एक चाल की आवश्यकता है, लेकिन कच्चे सूचक से नहीं। जब तक सी ++ 17 में चीजें बदली नहीं जातीं? –

6

आप ऐसा कर सकते हैं। हालांकि, आप इसे असाइन नहीं कर सकते हैं। इस प्रकार आप ऐसा करने के लिए कहते हैं, "मैं दृश्य बदलना चाहता हूं," बहुत संदिग्ध लगता है। मेरी राय में बेहतर तरीका उस ऑब्जेक्ट के लिए होगा जो उस दृश्य को बदलने के लिए दृश्य रखता है।

बेशक, आप आरएआईआई ऑब्जेक्ट्स का उपयोग कर रहे हैं और इसलिए आपको वास्तव में डिलीट कॉल करने की ज़रूरत नहीं है ... सही?

+1

+1। –

19

इसकी अनुमति है (इसके बाद ऑब्जेक्ट का उपयोग न करें), लेकिन मैं इस तरह के कोड को अभ्यास पर नहीं लिखूंगा। मुझे लगता है कि delete this केवल उन कार्यों में दिखाई देना चाहिए जिन्हें release या Release कहा जाता है और ऐसा लगता है: void release() { ref--; if (ref<1) delete this; }

+0

जो मेरी हर परियोजना में बिल्कुल एक बार है ... :-) – cmaster

12

खैर, घटक ऑब्जेक्ट मॉडल (COM) delete this निर्माण Release विधि कहा जाता है कि जब भी आप aquisited वस्तु रिलीज़ करना चाहते का एक हिस्सा हो सकता है में:

void IMyInterface::Release() 
{ 
    --instanceCount; 
    if(instanceCount == 0) 
     delete this; 
} 
3

यह वह जगह है एक पुराने, उत्तर दिया, सवाल है, लेकिन @ एलेक्सेंड्रे ने पूछा, "कोई ऐसा क्यों करना चाहेगा?", और मैंने सोचा कि मैं एक उदाहरण उपयोग प्रदान कर सकता हूं जिसे मैं आज दोपहर पर विचार कर रहा हूं।

विरासत कोड। अंत में एक हटा ओबीजे के साथ नग्न पॉइंटर्स Obj * obj का उपयोग करता है।

दुर्भाग्य से मुझे ऑब्जेक्ट को ज़िंदा रखने के लिए कभी-कभी, कभी-कभी आवश्यकता नहीं होती है।

मैं इसे एक संदर्भित स्मार्ट पॉइंटर बनाने पर विचार कर रहा हूं। लेकिन अगर ref_cnt_ptr<Obj> हर जगह उपयोग करना था, तो बहुत सारे कोड बदलने के लिए होगा। और यदि आप नग्न Obj * और ref_cnt_ptr को मिलाते हैं, तो आप ऑब्जेक्ट को पूरी तरह से हटा सकते हैं जब अंतिम ref_cnt_ptr चला जाता है, भले ही Obj * अभी भी जीवित है।

तो मैं एक clear_delete_ref_cnt_ptr बनाने के बारे में सोच रहा हूं। अर्थात। एक संदर्भ गिनती सूचक जहां डिलीट केवल एक स्पष्ट हटाने दिनचर्या में किया जाता है। इसे उसी स्थान पर उपयोग करना जहां मौजूदा कोड वस्तु के जीवनकाल को जानता है, साथ ही साथ मेरे नए कोड में जो ऑब्जेक्ट को ज़िंदा रहता है।

संदर्भ गणना को बढ़ाने और घटाने के रूप में clear_delete_ref_cnt_ptr को छेड़छाड़ की जाती है।

लेकिन स्पष्ट नहीं है जब संदर्भ गणना को clear_delete_ref_cnt_ptr विनाशक में शून्य माना जाता है।

केवल तभी खाली होना जब संदर्भ गणना को स्पष्ट हटाए जाने वाले ऑपरेशन में शून्य माना जाता है। जैसे कुछ में:

template<typename T> class explicit_delete_ref_cnt_ptr { 
private: 
    T* ptr; 
    int rc; 
    ... 
public: 
    void delete_if_rc0() { 
     if(this->ptr) { 
     this->rc--; 
     if(this->rc == 0) { 
      delete this->ptr; 
     } 
     this->ptr = 0; 
     } 
    } 
}; 

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

लेकिन अभी तक इसे हटाने की कोई आवश्यकता नहीं है।

लेकिन फिर यह मेरे लिए हुआ: यदि ऑब्जेक्ट की ओर इशारा किया गया है, तो पॉइंटी जानता है कि इसका संदर्भ गिना जा रहा है, उदा। यदि गिनती ऑब्जेक्ट (या किसी अन्य तालिका में) के अंदर है, तो नियमित delete_if_rc0 पॉइंट ऑब्जेक्ट का एक तरीका हो सकता है, न कि (स्मार्ट) पॉइंटर।

class Pointee { 
private: 
    int rc; 
    ... 
public: 
    void delete_if_rc0() { 
     this->rc--; 
     if(this->rc == 0) { 
      delete this; 
     } 
     } 
    } 
}; 

वास्तव में, यह बिल्कुल एक सदस्य विधि होने की जरूरत नहीं है, लेकिन एक नि: शुल्क समारोह हो सकता है:

map<void*,int> keepalive_map; 
template<typename T> 
void delete_if_rc0(T*ptr) { 
     void* tptr = (void*)ptr; 
     if(keepalive_map[tptr] == 1) { 
      delete ptr; 
     } 
}; 

(Btw, मैं जानता हूँ कि कोड बहुत सही नहीं है - यह हो जाता है यदि मैं सभी विवरण जोड़ता हूं तो कम पठनीय, इसलिए मैं इसे इस तरह से छोड़ रहा हूं।)

19

सी ++ डिज़ाइन किए गए कारणों में से एक कारण कोड का पुन: उपयोग करना आसान बनाना था। सामान्य रूप से, सी ++ लिखा जाना चाहिए ताकि यह काम करता है कि वर्ग को ढेर पर, सरणी में या ढेर पर तत्काल किया जाता है या नहीं। "इसे हटाएं" एक बहुत ही खराब कोडिंग अभ्यास है क्योंकि यह केवल तभी काम करेगा जब ढेर पर एक ही उदाहरण परिभाषित किया गया हो; और वहां एक और डिलीट स्टेटमेंट बेहतर नहीं था, जिसका प्रयोग ज्यादातर डेवलपर्स द्वारा ढेर को साफ करने के लिए किया जाता है। ऐसा करने से यह भी लगता है कि भविष्य में कोई रखरखाव प्रोग्रामर एक हटाए गए कथन को जोड़कर झूठी कथित स्मृति रिसाव का इलाज करेगा।

भले ही आप पहले से जानते हों कि आपकी वर्तमान योजना केवल ढेर पर एक ही उदाहरण आवंटित करना है, तो क्या होगा अगर भविष्य में कुछ खुश-भाग्यशाली डेवलपर आते हैं और ढेर पर एक उदाहरण बनाने का फैसला करते हैं? या, क्या होगा यदि वह कक्षा के कुछ हिस्सों को एक नई कक्षा में कटौती और पेस्ट करता है जिसे वह ढेर पर उपयोग करना चाहता है? जब कोड "इसे हटा दें" तक पहुंच जाता है तो यह बंद हो जाएगा और इसे हटा देगा, लेकिन फिर जब वस्तु दायरे से बाहर हो जाती है, तो यह विनाशक को बुलाएगी। विनाशक फिर इसे हटाने की कोशिश करेगा और फिर आप को रोक दिया जाएगा। अतीत में, ऐसा कुछ करने से न केवल कार्यक्रम खराब हो जाएगा बल्कि ऑपरेटिंग सिस्टम और कंप्यूटर को रीबूट करने की आवश्यकता होगी। किसी भी मामले में, यह अत्यधिक अनुशंसित नहीं है और लगभग हमेशा से बचा जाना चाहिए। मुझे उस कोड को लिखने के लिए काम करने के लिए काम करने वाली कंपनी को हताश, गंभीरता से plastered, या वास्तव में नफरत करना होगा।

+3

+1। मैं समझ नहीं पा रहा हूं कि तुम क्यों नीचे गए थे। "सी ++ लिखा जाना चाहिए ताकि यह काम करता है कि कक्षा ढेर पर, सरणी में या ढेर पर तत्काल है या नहीं," बहुत अच्छी सलाह है। – Joh

+0

जोह - अपवोट के लिए धन्यवाद! मैं या तो समझ में नहीं आता ... –

+1

आप उस वस्तु को लपेट सकते हैं जिसे आप खुद को एक विशेष वर्ग में हटाना चाहते हैं जो ऑब्जेक्ट को हटा देता है और फिर स्वयं, और स्टैक आवंटन को रोकने के लिए इस तकनीक का उपयोग करें: http://stackoverflow.com/ प्रश्न/124880/यह-संभव-से-रोकथाम-स्टैक-आवंटन-ऑफ-ऑब्जेक्ट-एंड-केवल-अनुमति-0 -ऐसे समय होते हैं जब वास्तव में व्यवहार्य विकल्प नहीं होता है। मैंने इस तकनीक का उपयोग डीएलएल फ़ंक्शन द्वारा शुरू किए गए थ्रेड को स्वयं-डिलीट करने के लिए किया है, लेकिन डीएलएल फ़ंक्शन को थ्रेड सिरों से पहले वापस जाना होगा। –

5

संदर्भ-गणना वाली वस्तुओं के लिए यह मूल मुहावरे है।

संदर्भ-गणना निर्धारण निर्धारक कचरा संग्रह का एक मजबूत रूप है - यह सुनिश्चित करता है कि वस्तुओं को उनके लिए ऐसा करने के लिए 'स्मार्ट' पॉइंटर्स इत्यादि पर निर्भर रहने के बजाय अपने स्वयं के जीवनकाल का प्रबंधन किया जाए। अंतर्निहित वस्तु केवल "संदर्भ" स्मार्ट पॉइंटर्स के माध्यम से उपयोग की जाती है, जिसे डिज़ाइन किया गया है ताकि पॉइंटर्स वास्तविक ऑब्जेक्ट में सदस्य पूर्णांक (संदर्भ गणना) में वृद्धि और कमी कर सकें।

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

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

+0

आपके पास सिर्फ एक (सिंगलटन) वर्ग "आवंटक/कचरा कलेक्टर" क्यों नहीं हो सकता है, एक इंटरफ़ेस जिसके माध्यम से सभी आवंटन किए जाते हैं और उस कक्षा को आवंटित वस्तुओं की सभी संदर्भ गणना को संभालने दें? ऑब्जेक्ट्स को कचरा संग्रह कार्यों से परेशान करने के बजाय खुद को मजबूर करने के बजाय, जो कुछ उनके नामित उद्देश्य से पूरी तरह से असंबंधित है। – Lundin

+1

आप अपने ऑब्जेक्ट के स्थैतिक और ढेर आवंटन को प्रतिबंधित करने के लिए केवल विनाशक को संरक्षित कर सकते हैं। – Balk

0

यह तब तक वैध है जब तक ऑब्जेक्ट ढेर में है। आपको वस्तु को केवल ढेर होने की आवश्यकता होगी। ऐसा करने का एकमात्र तरीका विनाशक को संरक्षित करना है - इस तरह से हटाएं केवल कक्षा से ही बुलाया जा सकता है, इसलिए आपको एक विधि की आवश्यकता होगी जो

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