आखिरकार और विध्वंसक के बीच वैचारिक अंतर क्या है?


12

सबसे पहले, मैं अच्छी तरह से जानता हूं कि C ++ में कोई 'अंत में' निर्माण क्यों नहीं है? लेकिन एक अन्य प्रश्न पर एक लंबी-बढ़ती टिप्पणी चर्चा एक अलग प्रश्न को स्पष्ट करती है।

इस मुद्दे के अलावा कि finallyC # और Java में मूल रूप से केवल एक बार (== 1) प्रति स्कोप मौजूद हो सकता है और एक सिंगल स्कोप में मल्टीपल (== n) C ++ डिस्ट्रक्टर्स हो सकते हैं, मुझे लगता है कि वे मूल रूप से एक ही चीज हैं। (कुछ तकनीकी अंतरों के साथ)

हालांकि, एक अन्य उपयोगकर्ता ने तर्क दिया :

... मैं यह कहना चाह रहा था कि एक डीओआर स्वाभाविक रूप से (रिलीज सेमैटिक्स) के लिए एक उपकरण है और अंत में स्वाभाविक रूप से (कमेंट शब्दार्थ) के लिए एक उपकरण है। यदि आप यह नहीं देखते हैं कि: इस बात पर विचार करें कि अंत में ब्लॉक में एक दूसरे के ऊपर अपवाद फेंकना वैध क्यों है, और विध्वंसक के लिए भी ऐसा क्यों नहीं है। (कुछ अर्थों में, यह एक डेटा बनाम नियंत्रण की बात है। विनाशकारी डेटा जारी करने के लिए हैं, अंत में रिलीज़ करने के लिए है। वे अलग हैं; यह दुर्भाग्यपूर्ण है कि C ++ उन्हें एक साथ जोड़ता है।)

क्या कोई इसे साफ कर सकता है?

जवाबों:


6
  • लेन-देन ( try)
  • त्रुटि आउटपुट / प्रतिक्रिया ( catch)
  • बाहरी त्रुटि ( throw)
  • प्रोग्रामर त्रुटि ( assert)
  • रोलबैक (निकटतम बात भाषाओं में गुंजाइश गार्ड हो सकती है जो उन्हें मूल रूप से समर्थन करती हैं)
  • संसाधन जारी करना (विध्वंसक)
  • विविध लेनदेन-स्वतंत्र नियंत्रण प्रवाह ( finally)

finallyमिसक लेन-देन-स्वतंत्र नियंत्रण प्रवाह की तुलना में बेहतर विवरण के साथ नहीं आ सकता है । यह जरूरी नहीं है कि लेन-देन और त्रुटि सुधार मानसिकता के संदर्भ में किसी भी उच्च-स्तरीय अवधारणा के लिए सीधे मैप करें, विशेष रूप से एक सैद्धांतिक भाषा में जिसमें विध्वंसक और दोनों हैं finally

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

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


4

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

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

हालांकि ऐसा करना बहुत अच्छा नहीं होगा, और भले ही .NET ने मैन्युअल रूप से प्रबंधित विध्वंसक के रूप में IDispose की शुरुआत की, और उस मैनुअल प्रबंधन को थोड़ा आसान बनाने के प्रयास के रूप में ब्लॉक का उपयोग करते हुए, यह अभी भी कुछ ऐसा नहीं है जिसे आप अभ्यास में करना चाहते हैं। ।


4

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

सी ++ कार्यक्रमों में प्रोग्रामर आवंटित संसाधनों को जारी करने के लिए जिम्मेदार है। यह आमतौर पर एक वर्ग के विध्वंसक में लागू किया जाता है और तुरंत किया जाता है जब एक चर दायरे से बाहर हो जाता है या जब डिलीट कहा जाता है।

जब c ++ में एक वर्ग का स्थानीय चर newउस उदाहरण के संसाधनों का उपयोग किए बिना बनाया जाता है, जब अपवाद होता है, तो विध्वंसक द्वारा निकाले जाते हैं।

// c++
void test() {
    MyClass myClass(someParameter);
    // if there is an exception the destructor of MyClass is called automatically
    // this does not work with
    // MyClass* pMyClass = new MyClass(someParameter);

} // on test() exit the destructor of myClass is implicitly called

जावा में, c # और अन्य सिस्टम एक स्वचालित मेमोरी मैनेजमेंट के साथ सिस्टम कचरा कलेक्टर तय करते हैं कि एक क्लास इंस्टेंस नष्ट हो जाए।

// c#
void test() {
    MyClass myClass = new MyClass(someParameter);
    // if there is an exception myClass is NOT destroyed so there may be memory/resource leakes

    myClass.destroy(); // this is never called
}

इसका कोई निहित तंत्र नहीं है, इसलिए आपको अंत में कोशिश का उपयोग करके स्पष्ट रूप से कार्यक्रम करना होगा

// c#
void test() {
    MyClass myClass = null;

    try {
        myClass = new MyClass(someParameter);
        ...
    } finally {
        // explicit memory management
        // even if there is an exception myClass resources are freed
        myClass.destroy();
    }

    myClass.destroy(); // this is never called
}

सी ++ में, विध्वंसक को केवल एक स्टैक ऑब्जेक्ट के साथ ही कहा जाता है और अपवाद के मामले में ढेर ऑब्जेक्ट के साथ क्यों नहीं?
जियोर्जियो

@ जियोर्जियो क्योंकि ढेर संसाधन एक मेमोरी स्पेस में रहते हैं जो सीधे कॉल स्टैक से बंधा नहीं है। उदाहरण के लिए, 2 थ्रेड्स के साथ एक मल्टीथ्रेडेड एप्लिकेशन की कल्पना करें, Aऔर B। यदि एक थ्रेड फेंकता है, तो रोलिंग A'sलेन-देन को आवंटित संसाधनों को नष्ट नहीं करना चाहिए B, जैसे - थ्रेड स्टेट्स एक दूसरे से स्वतंत्र हैं, और ढेर पर रहने वाली लगातार मेमोरी दोनों से स्वतंत्र है। हालाँकि, आमतौर पर C ++ में, ढेर मेमोरी अभी भी स्टैक पर ऑब्जेक्ट से जुड़ी हुई है।

@Giorgio उदाहरण के लिए, एक std::vectorऑब्जेक्ट स्टैक पर रह सकता है लेकिन ढेर पर मेमोरी को इंगित करता है - दोनों वेक्टर ऑब्जेक्ट (स्टैक पर) और इसकी सामग्री (ढेर पर) स्टैक के दौरान निपटा जाएगा, क्योंकि उस मामले में, स्टैक पर वेक्टर को नष्ट करने से एक विनाशकारी होता है जो कि ढेर पर संबंधित मेमोरी को मुक्त करता है (और इसी तरह उन ढेर तत्वों को नष्ट कर देता है)। आमतौर पर अपवाद-सुरक्षा के लिए, अधिकांश C ++ ऑब्जेक्ट्स स्टैक पर रहते हैं, भले ही वे केवल ढेर पर मेमोरी को इंगित करने वाले हैंडल हों, दोनों ढेर को ढेर करने और स्टैक मेमोरी को स्टैक को खाली करने की प्रक्रिया को स्वचालित करते हैं।

4

खुशी है कि आपने इसे एक प्रश्न के रूप में पोस्ट किया है। :)

मैं यह कहना चाह रहा था कि विध्वंसक और finallyवैचारिक रूप से भिन्न होते हैं:

  • विध्वंसक संसाधन जारी करने के लिए हैं ( डेटा )
  • finallyकॉलर ( नियंत्रण ) पर लौटने के लिए है

इस काल्पनिक छद्म कोड पर विचार करें, कहें:

try {
    bar();
} finally {
    logfile.print("bar has exited...");
}

finallyयहां पूरी तरह से एक नियंत्रण समस्या का समाधान हो रहा है न कि संसाधन प्रबंधन की समस्या का।
विभिन्न कारणों से विध्वंसक में ऐसा करने का कोई मतलब नहीं होगा:

  • कोई भी चीज़ "हासिल" या "निर्मित" नहीं की जा रही है
  • लॉग फ़ाइल को मुद्रित करने के लिए विफलता होगा नहीं संसाधन लीक, डेटा भ्रष्टाचार, आदि (यह सोचते हैं कि यहाँ लॉगफ़ाइल कहीं और कार्यक्रम में वापस खिलाया नहीं है) में परिणाम
  • यह logfile.printविफल होने के लिए वैध है , जबकि विनाश (वैचारिक रूप से) विफल नहीं हो सकता

यहाँ एक और उदाहरण है, इस बार की तरह जावास्क्रिप्ट में:

var mo_document = document, mo;
function observe(mutations) {
    mo.disconnect();  // stop observing changes to prevent re-entrance
    try {
        /* modify stuff */
    } finally {
        mo.observe(mo_document);  // continue observing (conceptually, this can fail)
    }
}
mo = new MutationObserver(observe);
return observe();

उपरोक्त उदाहरण में, फिर से, जारी करने के लिए कोई संसाधन नहीं हैं।
वास्तव में, finallyब्लॉक अपने लक्ष्य को प्राप्त करने के लिए आंतरिक रूप से संसाधनों का अधिग्रहण कर रहा है, जो संभावित रूप से विफल हो सकते हैं। इसलिए, एक विध्वंसक (यदि जावास्क्रिप्ट एक था) का उपयोग करने का कोई मतलब नहीं है।

दूसरी ओर, इस उदाहरण में:

b = get_data();
try {
    a.write(b);
} finally {
    free(b);
}

finallyएक संसाधन को नष्ट कर रहा है, b। यह डेटा की समस्या है। समस्या स्पष्ट रूप से कॉलर पर नियंत्रण वापस करने के बारे में नहीं है, बल्कि संसाधन लीक से बचने के बारे में है।
विफलता एक विकल्प नहीं है, और (वैचारिक रूप से) कभी नहीं होना चाहिए।
हर रिलीज़ bको जरूरी अधिग्रहण के साथ जोड़ा जाता है, और यह RAII का उपयोग करने के लिए समझ में आता है।

दूसरे शब्दों में, सिर्फ इसलिए कि आप या तो अनुकरण करने के लिए उपयोग कर सकते हैं इसका मतलब यह नहीं है कि दोनों एक और एक ही समस्या हैं या दोनों ही दोनों समस्याओं का उचित समाधान हैं।


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

2
इस तथ्य को कैसे finallyइस्तेमाल किया जाता है जो ज्यादातर (गैर-मेमोरी) संसाधनों के कारक को जारी करने के लिए उपयोग किया जाता है?
बार्ट वैन इनगेन शेनौ

1
@BartvanIngenSchenau: मैंने कभी यह तर्क नहीं दिया कि वर्तमान में अस्तित्व में किसी भी भाषा में एक दर्शन या कार्यान्वयन है जो मेरे वर्णित वर्णन से मेल खाता है। लोगों ने हर उस चीज का आविष्कार नहीं किया है जो संभवतः अभी तक मौजूद हो सकती है। मैंने केवल यह तर्क दिया कि दोनों धारणाओं को अलग करने में मूल्य होगा क्योंकि वे अलग-अलग विचार हैं और अलग-अलग उपयोग के मामले हैं। आपकी जिज्ञासा को पूरा करने के लिए, मेरा मानना ​​है कि डी में दोनों हैं। शायद अन्य भाषाएं भी हैं। मैं इसे प्रासंगिक नहीं मानता, लेकिन मैं इस बात की कम परवाह नहीं कर सकता था कि उदाहरण के लिए जावा किसके पक्ष में था finally
user541686

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

1
@ dan04: बहुत बहुत धन्यवाद, यह इसके लिए एकदम सही उदाहरण है। मैं कसम खा सकता हूं कि मैं ऐसी कई स्थितियों में आ जाऊंगा जहां RAII को कोई मतलब नहीं था, लेकिन मुझे उनके बारे में सोचने में इतना कठिन समय था।
user541686

1

k3b का जवाब वास्तव में यह अच्छी तरह से वाक्यांश:

सी ++ में एक विध्वंसक एक अंतर्निहित तंत्र है (स्वचालित रूप से आह्वान) आवंटित संसाधनों को जारी करने के लिए प्रयास करते समय ... आखिरकार ऐसा करने के लिए एक स्पष्ट तंत्र के रूप में इस्तेमाल किया जा सकता है।

"संसाधन" के रूप में, मैं जॉन कल्ब को संदर्भित करना पसंद करता हूं : आरएआईआई का मतलब जिम्मेदारी अधिग्रहण अधिग्रहण प्रारंभिक होना चाहिए

वैसे भी, जैसा कि स्पष्ट है कि स्पष्ट रूप से ऐसा प्रतीत होता है:

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

मुझे लगता है कि यह वैचारिक भाग के लिए है ...


... अब IMHO के कुछ दिलचस्प विवरण हैं:

मैं यह भी नहीं सोचता कि विध्वंसक में कुछ कोड को चलाने की जिम्मेदारी के अलावा c'tor / d'tor को वैचारिक रूप से "अधिग्रहण" या "कुछ भी" बनाने की आवश्यकता नहीं है। जो आखिर में भी है: कुछ कोड चलाते हैं।

और जब एक अंतिम ब्लॉक में कोड निश्चित रूप से एक अपवाद को फेंक सकता है, तो यह कहना मेरे लिए पर्याप्त अंतर नहीं है कि वे स्पष्ट रूप से स्पष्ट बनाम निहितार्थ से अलग हैं।

(साथ ही, मैं बिल्कुल भी आश्वस्त नहीं हूं कि "अच्छा" कोड अंततः से फेंक देना चाहिए - शायद यह एक और संपूर्ण प्रश्न है।)


मेरे जावास्क्रिप्ट उदाहरण पर आपके विचार क्या हैं?
user541686

आपके अन्य तर्कों के बारे में: "क्या हम वास्तव में एक ही चीज को अनियमित रूप से लॉग इन करना चाहेंगे?" हां, यह सिर्फ एक उदाहरण है और आप बिंदु को याद करने की तरह हैं, और हां, प्रत्येक मामले के लिए अधिक विशिष्ट विवरणों को लॉग इन करने से किसी ने कभी भी निषिद्ध नहीं किया है। यहाँ मुद्दा यह है कि आप निश्चित रूप से दावा नहीं कर सकते कि कभी भी ऐसी स्थिति नहीं है जिसमें आप कुछ ऐसा करना चाहते हैं जो दोनों के लिए सामान्य हो। कुछ लॉग प्रविष्टियाँ सामान्य हैं, कुछ विशिष्ट हैं; तुम दोनों चाहते हो और फिर, आप लॉगिंग पर ध्यान केंद्रित करके बिंदु को पूरी तरह से गायब कर रहे हैं। 10-लाइन उदाहरणों को प्रेरित करना कठिन है; कृपया इस बिंदु को याद न करने का प्रयास करें।
user541686

आपने इन्हें कभी संबोधित नहीं किया ...
user541686

@ मेहरदाद - मैंने आपके जावास्क्रिप्ट उदाहरण को संबोधित नहीं किया क्योंकि यह मुझे इस बारे में चर्चा करने के लिए एक और पेज लेगा। (मैंने कोशिश की, लेकिन यह मुझे बहुत लंबा लगा, कुछ सुसंगत वाक्यांश के लिए जिसे मैंने इसे छोड़ दिया :-):
मार्टिन बा

@ मेहरदाद - अपने अन्य बिंदुओं के लिए - ऐसा लगता है कि हमें असहमत होने के लिए सहमत होना होगा। मैं देख रहा हूं कि आप अंतर के साथ कहां लक्ष्य कर रहे हैं, लेकिन मुझे यकीन नहीं है कि वे वैचारिक रूप से कुछ अलग हैं: मुख्य रूप से क्योंकि मैं ज्यादातर शिविर में हूं जो सोचता है कि आखिरकार फेंकना एक बहुत बुरा विचार है ( ध्यान दें : मैं भी अपने observerउदाहरण में सोचें कि वहाँ फेंकना वास्तव में एक बुरा विचार होगा।) एक चैट खोलने के लिए स्वतंत्र महसूस करें, यदि आप इस पर आगे चर्चा करना चाहते हैं। आपके तर्कों के बारे में सोचकर यह निश्चित रूप से मज़ेदार था। चीयर्स।
मार्टिन बा ०
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.