क्या इसे हटाने की अनुमति है?


232

क्या यह अनुमति दी जाती है delete this;कि यदि डिलीट-स्टेटमेंट आखिरी स्टेटमेंट है जिसे क्लास के उस उदाहरण पर निष्पादित किया जाएगा? बेशक, मुझे यकीन है कि this-pointer द्वारा प्रस्तुत वस्तु गीत new-निर्मित है।

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

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;
}

क्या मैं यह कर सकता हूं?


13
मुख्य समस्या यह होगी कि यदि आपने delete thisकक्षा के बीच एक तंग युग्मन बनाया है और उस वर्ग की वस्तुओं को बनाने के लिए उपयोग की जाने वाली आवंटन विधि। यह बहुत ही खराब OO डिजाइन है, क्योंकि OOP में सबसे बुनियादी चीज स्वायत्त कक्षाएं बनाना है जो कि यह नहीं जानते या परवाह नहीं करते हैं कि उनका कॉलर क्या कर रहा है। इस प्रकार एक ठीक से डिज़ाइन की गई कक्षा को यह पता नहीं होना चाहिए कि उसे किस तरह से आवंटित किया गया है। यदि आपको किसी कारणवश इस तरह के अजीबोगरीब तंत्र की आवश्यकता है, तो मुझे लगता है कि वास्तविक वर्ग के आसपास रैपर क्लास का उपयोग करने के लिए बेहतर डिज़ाइन होगा, और रैपर को आवंटन से निपटने दें।
लुंडिन

में नहीं हटा सकते setWorkingModule?
जिमी टी।

जवाबों:


238

C ++ FAQ Lite में इसके लिए विशेष रूप से एक प्रविष्टि है

मुझे लगता है कि यह उद्धरण इसे अच्छी तरह से बताता है

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


15
इसी एफक्यूए में कुछ उपयोगी टिप्पणी भी है: yosefk.com/c++fqa/heap.html#fqa-16.15
एलेक्जेंडर सी।

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

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

3
लिंक किए गए FAQ लेख में 'सावधान' को परिभाषित किया गया है। (जबकि FQA लिंक ज्यादातर किराए पर है - जैसे लगभग सब कुछ - इसमें C ++ कितना बुरा है)
चारोनएक्स

88

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

एक आभासी विध्वंसक (एक निहित परिभाषा (15.8) सहित) की परिभाषा के बिंदु पर, गैर-सरणी विकेन्द्रीकरण फ़ंक्शन निर्धारित किया जाता है जैसे कि अभिव्यक्ति को नष्ट करने वाले वर्ग के गैर-आभासी विध्वंसक में दिखाई देने वाले को हटा दें (8.3.5 देखें) )।

यह इस विचार का समर्थन करता है कि मानक delete this;वैध मानता है - यदि यह अमान्य था, तो इसका प्रकार सार्थक नहीं होगा। delete this;जहाँ तक मुझे पता है, यह मानक उल्लेखों का एकमात्र स्थान है।

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

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

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


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

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

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

1
@MBraedley: मैंने भी ऐसा ही किया है, लेकिन जो मुझे कीचड़ लगता है, उससे बचना पसंद करते हैं।
जेरी कॉफिन

किसी के लिए जो परवाह कर सकता है ... वहाँ एक बहुत अच्छा मौका है कि इसे कोड द्वारा (कम से कम भाग में) संभाला जा रहा है जो वास्तव में करता है this। हां, कोड बिल्कुल उसी के द्वारा संभाला जा रहा है this। ;)
गैलेक्सी

46

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

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);
}

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


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

@ आप निश्चित नहीं हैं कि आपका क्या मतलब है, यह मेरे लिए काम करता है: ideone.com/aavQUKदूसरेunique_ptr से बनाने के लिए एक कदम की आवश्यकता होती है, लेकिन कच्चे सूचक से नहीं। जब तक C ++ 17 में चीजें नहीं बदलीं? unique_ptr
मार्क रैनसम

आह सी ++ 14, यही कारण है कि होगा। मुझे अपने देव बॉक्स पर अपना सी ++ अपडेट करना होगा। मैं अपने हाल ही में उभरे हुए gentoo सिस्टम पर आज रात फिर से कोशिश करूँगा!
उल्लू

25

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

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


7
+1। मुझे समझ नहीं आ रहा है कि आपको क्यों अपमानित किया गया। "सी ++ लिखा जाना चाहिए ताकि यह काम करे कि क्या वर्ग ढेर पर, सरणी में, या स्टैक पर त्वरित है" बहुत अच्छी सलाह है।
जोह

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

आप इस तरह से प्रोग्राम नहीं कर सकते हैं कि कोई व्यक्ति आपके कोड के साथ सिर्फ कॉपी और पेस्ट करे, वैसे भी इसका गलत इस्तेमाल करता है
जिमी टी।

22

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


जो कि मेरा हर प्रोजेक्ट में एक बार होता है ... :-)
cmaster - reinstate monica

15

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

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

8

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

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

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

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


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

1
आप बस अपनी वस्तु के स्थैतिक और स्टैक आवंटन को रोकने के लिए विध्वंसक को संरक्षित कर सकते हैं।
Game_Overture

7

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

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


4

यह एक पुराना, उत्तर दिया गया, प्रश्न है, लेकिन @Alexandre ने पूछा "कोई भी ऐसा क्यों करना चाहेगा?"

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

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

मैं इसे एक संदर्भ स्मार्ट पॉइंटर गिने जाने पर विचार कर रहा हूं। लेकिन बहुत सारे कोड बदलने होंगे, अगर मुझे ref_cnt_ptr<Obj>हर जगह इस्तेमाल करना था । और यदि आप नग्न ओब्ज * और रेफ_cnt_ptr मिलाते हैं, तो आप ऑब्जेक्ट को स्पष्ट रूप से हटा सकते हैं जब अंतिम 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;
      }
    }
 };

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

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

लेकिन फिर यह मेरे साथ हुआ: यदि ऑब्जेक्ट ने इंगित किया, तो पॉइंटीटी को पता है कि यह संदर्भ में गिना जा रहा है, उदाहरण के लिए यदि गिनती ऑब्जेक्ट के अंदर है (या किसी अन्य तालिका में), तो नियमित रूप से 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, मुझे पता है कि कोड काफी सही नहीं है - अगर मैं सभी विवरण जोड़ देता हूं तो यह कम पठनीय हो जाता है, इसलिए मैं इसे इस तरह से छोड़ रहा हूं।)


0

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.