क्या a ट्राइ… कैच… आखिर ’का the आखिर’ हिस्सा भी जरूरी है?


25

कुछ भाषाएं (जैसे C ++ और PHP के शुरुआती संस्करण) finallyएक try ... catch ... finallyनिर्माण के हिस्से का समर्थन नहीं करती हैं । क्या finallyकभी जरूरी है? क्योंकि इसमें कोड हमेशा चलता रहता है, मैं उस कोड को try ... catchबिना किसी finallyखंड के ब्लॉक के बाद क्यों नहीं रख सकता / नहीं ? क्यों एक का उपयोग करें? (मैं उपयोग करने / उपयोग न करने के लिए एक कारण / प्रेरणा की तलाश कर रहा हूं finally, न कि 'कैच' से दूर होने का कारण या ऐसा करने का अधिकार।


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
maple_shaft

जवाबों:


36

दूसरों ने जो कहा है, इसके अलावा, यह एक अपवाद के लिए भी संभव है कि पकड़ क्लॉज के अंदर फेंक दिया जाए। इस पर विचार करो:

try { 
    throw new SomeException();
} catch {
    DoSomethingWhichUnexpectedlyThrows();
}
Cleanup();

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


4
एक संक्षिप्त और सीधे उत्तर के लिए धन्यवाद जो सिद्धांत में नहीं पचता है और 'भाषा X वाई' क्षेत्र से बेहतर है।
एजी हैमरथिफ़

56

जैसा कि दूसरों ने उल्लेख किया है, कोई भी गारंटी नहीं है कि एक tryबयान के बाद कोड तब तक निष्पादित होगा जब तक कि आप हर संभव अपवाद को नहीं पकड़ते। इसने कहा, यह:

try {
   mightThrowSpecificException();
} catch (SpecificException e) {
   handleError();
} finally {
   cleanUp();
}

1 के रूप में फिर से लिखा जा सकता है :

try {
   mightThrowSpecificException();
} catch (SpecificException e) {
   try {
       handleError();
   } catch (Throwable e2) {
       cleanUp();
       throw e2;
   }
} catch (Throwable e) {
   cleanUp();
   throw e;
}
cleanUp();

लेकिन उत्तरार्द्ध को आपको सभी अखंडित अपवादों को पकड़ने की आवश्यकता है, सफाई कोड की नकल करें और फिर से फेंकना याद रखें। तो finallyयह आवश्यक नहीं है , लेकिन यह उपयोगी है

C ++ के पास नहीं है finallyक्योंकि Bazne Stroustrup का मानना ​​है कि RAII बेहतर है , या कम से कम अधिकांश मामलों के लिए पर्याप्त है:

C ++ एक "अंततः" निर्माण क्यों प्रदान नहीं करता है?

क्योंकि C ++ एक विकल्प का समर्थन करता है जो लगभग हमेशा बेहतर होता है: "संसाधन अधिग्रहण आरंभीकरण है" तकनीक (TC ++ PL3 अनुभाग 14.4)। मूल विचार एक स्थानीय वस्तु द्वारा एक संसाधन का प्रतिनिधित्व करना है, ताकि स्थानीय वस्तु का विनाशकर्ता संसाधन को मुक्त कर दे। इस तरह, प्रोग्रामर संसाधन जारी करना नहीं भूल सकता।


1 स्टैक ट्रेस जानकारी को खोए बिना सभी अपवादों और पुनर्विचार को पकड़ने के लिए विशिष्ट कोड भाषा द्वारा भिन्न होता है। मैंने जावा का उपयोग किया है, जहां अपवाद निर्मित होने पर स्टैक ट्रेस कैप्चर किया जाता है। C # में आप सिर्फ उपयोग करेंगे throw;


8
आपको handleError()दूसरे मामले में अपवादों को भी पकड़ना है , नहीं?
जूरी रॉबल

1
आप एक त्रुटि भी फेंक सकते हैं। मैं catch (Throwable t) {}कोशिश करूँगा कि , कोशिश के साथ .. पूरे प्रारंभिक ब्लॉक के चारों ओर ब्लॉक पकड़ ( handleErrorसाथ ही साथ फेंकने वाले पकड़ने के लिए )
njzk2

1
मैं वास्तव में आपके द्वारा छोड़े गए अतिरिक्त प्रयास-कैच को जोड़ूंगा, handleErro();जो कॉल करते समय इसे और भी बेहतर तर्क देगा कि आखिरकार ब्लॉक उपयोगी हैं (भले ही यह मूल प्रश्न नहीं था)।
एलेक्स

1
यह उत्तर वास्तव में C ++ के पास नहीं है के प्रश्न को संबोधित नहीं करता है finally, जो बहुत अधिक बारीक है।
11:16 बजे डेडएमजी

1
@Agi Hammerthief नेस्टेड विशिष्ट अपवाद tryके catchलिए अंदर है। दूसरे, यह संभव है कि आप यह नहीं जानते कि क्या आप सफलतापूर्वक त्रुटि को संभाल सकते हैं जब तक कि आपने अपवाद की जांच नहीं की है, या अपवाद का कारण भी आपको त्रुटि को संभालने से रोकता है (कम से कम उस स्तर पर)। I / O करते समय यह काफी सामान्य है। रीथ्रो वहाँ है क्योंकि रन की गारंटी देने का एकमात्र तरीका सब कुछ पकड़ना है , लेकिन मूल कोड ब्लॉक में उत्पन्न होने वाले अपवादों को ऊपर की ओर फैलाने की अनुमति देगा । cleanUpcatch (SpecificException e)
डोभाल

22

finally ब्लॉक आमतौर पर संसाधनों को साफ करने के लिए उपयोग किया जाता है जो कई रिटर्न स्टेटमेंट का उपयोग करते समय पठनीयता के साथ मदद कर सकते हैं:

int DoSomething() {
    try {
        open_connection();
        return get_result();
    }
    catch {
        return 2;
    }
    finally {
        close_connection();
    }
}

बनाम

int DoSomething() {
    int result;
    try {
        open_connection();
        result = get_result();
    }
    catch {
        result = 2;
    }
    close_connection();
    return result;
}

2
मुझे लगता है कि यह सबसे अच्छा जवाब है। एक सामान्य अपवाद के प्रतिस्थापन के रूप में अंतिम रूप से उपयोग करना केवल चमकदार लगता है। सही उपयोग मामला सफाई संसाधनों या अनुरूप संचालन के लिए है।
किक

3
शायद और भी आम कैच ब्लॉक के बजाय ट्राई ब्लॉक के अंदर लौट रहा है।
माइकल एंडरसन

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

15

जैसा कि आप स्पष्ट रूप से पहले ही समझ चुके हैं, हां, सी ++ उस तंत्र के बिना समान क्षमताएं प्रदान करता है। जैसे, सख्ती से बोलना, try/ finallyतंत्र वास्तव में आवश्यक नहीं है।

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

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

मुझे लगता है कि यह दावा करने वाले कि इस क्षमता के लिए C ++ सिंटैक्स जावा की तुलना में "कम अनुकूल" है, बल्कि बिंदु को याद नहीं कर रहे हैं। इससे भी बदतर, वे जिम्मेदारी के विभाजन के बारे में बहुत अधिक महत्वपूर्ण बिंदु याद कर रहे हैं जो वाक्यविन्यास से बहुत आगे निकल जाता है, और कोड को कैसे डिज़ाइन किया गया है, इसके बारे में बहुत कुछ किया है।

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

जावा (और समान) कुछ अलग हैं। हालांकि वे (तरह) समर्थन करते हैं finalizeजो सैद्धांतिक रूप से समान क्षमता प्रदान कर सकते हैं, समर्थन इतना कमजोर है कि यह मूल रूप से अनुपयोगी है (और वास्तव में, अनिवार्य रूप से कभी उपयोग नहीं किया गया)।

नतीजतन, वर्ग खुद को आवश्यक सफाई करने में सक्षम होने के बजाय, वर्ग के ग्राहक को ऐसा करने के लिए कदम उठाने की आवश्यकता होती है। यदि हम पर्याप्त रूप से अदूरदर्शी तुलना करते हैं, तो यह पहली नज़र में दिखाई दे सकता है कि यह अंतर काफी मामूली है और जावा इस संबंध में C ++ के साथ काफी प्रतिस्पर्धी है। हम कुछ इस तरह से समाप्त करते हैं। C ++ में, क्लास कुछ इस तरह दिखता है:

class Foo {
    // ...
public:
    void do_whatever() { if (xyz) throw something; }
    ~Foo() { /* handle cleanup */ }
};

... और ग्राहक कोड कुछ इस तरह दिखता है:

void f() { 
    Foo f;
    f.do_whatever();
    // possibly more code that might throw here
}

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

संक्षेप में, जावा दृष्टिकोण मूल रूप से गारंटी देता है कि हम प्रदान करने की कोशिश करने वाले कई सार "लीक" हैं - किसी भी और हर वर्ग को निर्धारक सफाई की आवश्यकता होती है जो वर्ग के ग्राहक को सफाई और क्या सफाई करने के तरीके के बारे में जानने के लिए बाध्य करता है। , बल्कि उन विवरणों को कक्षा में ही छिपाया जा रहा है।

हालांकि मैं यह "जावा दृष्टिकोण" कहा जाता है की है ऊपर, try/ finallyऔर अन्य नामों के तहत एक जैसी कार्यविधि पूरी तरह जावा के लिए सीमित नहीं हैं। एक प्रमुख उदाहरण के लिए, .NET भाषाओं (जैसे, C #) के अधिकांश (सभी?) समान प्रदान करते हैं।

जावा और सी # दोनों के हाल के पुनरावृत्तियों ने इस संबंध में "क्लासिक" जावा और सी ++ के बीच एक आधा बिंदु का कुछ भी प्रदान किया है। C # में, एक ऑब्जेक्ट जो अपने क्लीनअप को स्वचालित करना चाहता है IDisposable, इंटरफ़ेस को लागू कर सकता है, जो Disposeकि एक विधि प्रदान करता है जो (C कम से कम अस्पष्ट) C ++ विध्वंसक के समान है। जबकि इसका उपयोग जावा में / जैसे के माध्यम से किया जा सकता है , सी # कार्य को एक बयान के साथ थोड़ा और स्वचालित करता है जो आपको उन संसाधनों को परिभाषित करने देता है जो एक गुंजाइश के रूप में बनाए जाएंगे, और गुंजाइश से बाहर होने पर नष्ट हो जाएंगे। हालाँकि C ++ द्वारा प्रदान किए गए स्वचालन और निश्चितता के स्तर में अभी भी अच्छी तरह से कमी है, यह अभी भी जावा पर पर्याप्त सुधार है। विशेष रूप से, वर्ग डिजाइनर कैसे के विवरण को केंद्रीकृत कर सकता हैtryfinallyusingके कार्यान्वयन में वर्ग के निपटान के लिए IDisposable। क्लाइंट प्रोग्रामर के लिए जो कुछ भी बचा है, वह यह usingसुनिश्चित करने के लिए एक बयान लिखने का कम बोझ है कि IDisposableइंटरफ़ेस का उपयोग तब किया जाएगा जब यह होना चाहिए। जावा 7 और नए में, दोषियों को बचाने के लिए नाम बदल दिए गए हैं, लेकिन मूल विचार मूल रूप से समान है।


1
एकदम सही जवाब। विनाशकर्ता हैं सी ++ में आवश्यक है सुविधा।
थॉमस एडिंग

13

विश्वास नहीं कर सकता और कोई नहीं इस उठाया गया है (कोई यमक इरादा) - तुम नहीं है की जरूरत है एक पकड़ खंड!

यह पूरी तरह से उचित है:

try 
{
   AcquireManyResources(); 
   DoSomethingThatMightFail(); 
}
finally 
{
   CleanUpThoseResources(); 
}

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

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


9

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

अंत में ब्लॉक की मदद करना है और यह सुनिश्चित करना है कि कोई भी अपवाद क्लीनअप नहीं होगा।


4
यही कारण है कि एक के लिए पर्याप्त कारण नहीं है finallyके रूप में आप के साथ "अप्रत्याशित" अपवाद रोका जा सकता है, catch(Object)या catch(...)पकड़ने के alls।
MSalters

1
जो चारों ओर एक काम की तरह लगता है। वैचारिक रूप से अंत में क्लीनर है। हालांकि मुझे इसका उपयोग करने के लिए शायद ही कभी कबूल करना चाहिए।
जल्दी_नौज

7

कुछ भाषाएँ अपनी वस्तुओं के लिए कंस्ट्रक्टर और विध्वंसक दोनों की पेशकश करती हैं (जैसे C ++ मुझे विश्वास है)। इन भाषाओं के साथ आप finallyएक विध्वंसक में आमतौर पर जो कुछ किया जाता है, उसके अधिकांश (यकीनन सभी) कर सकते हैं । जैसे - उन भाषाओं में - एक finallyखंड अतिशयोक्तिपूर्ण हो सकता है।

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


यह ध्यान रखना उपयोगी हो सकता है कि विनाशकारी निर्धारक होने पर विनाशकारी संसाधनों की मदद करते हैं । यदि हमें पता नहीं है कि कब वस्तु को नष्ट किया जाएगा और / या कचरा एकत्र किया जाएगा, तो विनाशकारी पर्याप्त सुरक्षित नहीं हैं।
मोरविन

@ मॉरवेन - अच्छी बात है। मैंने इसे जावा के अपने संदर्भ के साथ संकेत दिया था, finaliseलेकिन मैं इस समय विनाशकारी / फाइनल के आसपास के राजनीतिक तर्कों में नहीं आना पसंद करूंगा।
OldCurmudgeon

C ++ में विनाश नियतात्मक है। जब एक स्वचालित वस्तु से बाहर निकलने की गुंजाइश होती है (जैसे यह स्टैक से पॉपप हो जाती है), तो इसके विनाशकर्ता को कहा जाता है। (सी ++ आपको ढेर पर वस्तुओं को आवंटित करने की अनुमति देता है, न कि केवल ढेर।)
रोब के

@RobK - और यह finaliseएक एक्स्टेंसिबल स्वाद और एक ऊप जैसे तंत्र दोनों के साथ एक की सटीक कार्यक्षमता है - finaliseअन्य भाषाओं के तंत्र के लिए बहुत ही अभिव्यंजक और तुलनीय है ।
OldCurmudgeon

1

अंत में कोशिश करें और पकड़ने की कोशिश दो अलग-अलग चीजें हैं जो केवल: "कोशिश" कीवर्ड को साझा करती हैं। व्यक्तिगत रूप से मैं इसे अलग देखना चाहूंगा। आप उन्हें एक साथ देखने का कारण यह है क्योंकि अपवाद एक "कूद" पैदा करते हैं।

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


3
.NET में उन्हें अलग-अलग तंत्रों का उपयोग करके लागू किया जाता है; जावा में, हालांकि, जेवीएम द्वारा मान्यता प्राप्त एकमात्र निर्माण "त्रुटि गोटो पर" शब्द के बराबर है, एक पैटर्न जो सीधे समर्थन करता है try catchलेकिन नहीं try finally; उत्तरार्द्ध का उपयोग करके कोड केवल पूर्व का उपयोग करके कोड में परिवर्तित किया जाता है, कोड के finallyसभी स्थानों में ब्लॉक की सामग्री को कॉपी करके जहां इसे निष्पादित करने की आवश्यकता हो सकती है।
सुपरकैट

@supercat अच्छा, जावा के बारे में अतिरिक्त जानकारी के लिए धन्यवाद।
पीटर बी

1

चूंकि यह प्रश्न C ++ को एक भाषा के रूप में निर्दिष्ट नहीं करता है, इसलिए मैं C ++ और जावा के मिश्रण पर विचार करूंगा, क्योंकि वे ऑब्जेक्ट विनाश के लिए एक अलग दृष्टिकोण लेते हैं, जो कि विकल्पों में से एक के रूप में सुझाया जा रहा है।

कारण, आप कोशिश करने वाले ब्लॉक के बाद कोड के बजाय अंततः ब्लॉक का उपयोग कर सकते हैं

  • आप कोशिश ब्लॉक से जल्दी लौटते हैं: इस पर विचार करें

    Database db = null;
    try {
     db = open_database();
     if(db.isSomething()) {
       return 7;
     }
     return db.someThingElse();
    } finally {
      if(db!=null)
        db.close();
    }
    

    के साथ तुलना:

    Database db = null;
    int returnValue = 0;
    try {
     db = open_database();
     if(db.isSomething()) {
       returnValue = 7;
     } else {
       returnValue = db.someThingElse();
     }
    } catch(Exception e) {
      if(db!=null)
        db.close();
    }
    return returnValue;
    
  • आप कैच ब्लॉक से जल्दी लौटते हैं: तुलना करें

    Database db = null;
    try {
     db = open_database();
     db.doSomething();
    } catch (DBIntegrityException e ) {
      return 7;
    } catch (DBIsADonkeyException e ) {
      return 11;
    } finally {
      if(db!=null)
        db.close();
    }
    

    बनाम:

    Database db = null;
    try {
     db = open_database();
     db.doSomething();
    } catch (DBIntegrityException e ) {
      if(db!=null) 
        db.close();
      return 7;
    } catch (DBIsADonkeyException e ) {
      if(db!=null)
        db.close();
      return 11;
    }           
    db.close();
    
  • आप अपवादों को फिर से उखाड़ फेंकते हैं। की तुलना करें:

    Database db = null;
    try {
     db = open_database();
     db.doSomething();
    } catch (DBIntegrityException e ) {
      throw convertToRuntimeException(e,"DB was wonkey");
    } finally {
      if(db!=null)
        db.close();
    }
    

    बनाम:

    Database db = null;
    try {
     db = open_database();
     db.doSomething();
    } catch (DBIntegrityException e ) {
      if(db!=null)
        db.close();
      throw convertToRuntimeException(e,"DB was wonkey");
    } 
    if(db!=null)
      db.close();
    

इन उदाहरणों से यह बहुत बुरा नहीं लगता है, लेकिन अक्सर आपके पास इनमें से कई मामलों में बातचीत होती है और एक से अधिक अपवाद / संसाधन प्रकार खेलते हैं। finallyएक पेचीदा रखरखाव दुःस्वप्न बनने से अपने कोड को बनाए रखने में मदद कर सकता है।

अब C ++ में इन्हें स्कोप बेस्ड ऑब्जेक्ट्स के साथ हैंडल किया जा सकता है। लेकिन IMO इस दृष्टिकोण के दो नुकसान हैं। वाक्य रचना कम अनुकूल है। 2. निर्माण के क्रम विनाश के क्रम के विपरीत होने से चीजें कम स्पष्ट हो सकती हैं।

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


1

एक प्रोग्रामिंग भाषा में "आवश्यक" सभी निर्देश हैं:

assignment a = b
subtract a from b
goto label
test a = 0
if true goto label

किसी भी एल्गोरिथ्म को केवल ऊपर दिए गए निर्देशों का उपयोग करके कार्यान्वित किया जा सकता है, अन्य सभी भाषा निर्माण कार्यक्रम को लिखने के लिए आसान बनाने और अन्य प्रोग्रामर के लिए अधिक समझने योग्य हैं।

इस तरह के न्यूनतम निर्देश सेट का उपयोग करके वास्तविक हार्डवेयर के लिए पुराना दुनियादार कंप्यूटर देखें ।


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

1
मुद्दा यह है कि किसी भी भाषा को लागू करने के लिए सिर्फ ये 5 ऑपरेशन किसी भी एल्गोरिथ्म को लागू कर सकते हैं - यद्यपि अत्याचारपूर्ण। उच्च स्तरीय भाषाओं में अधिकांश छद्म / ऑपरेटर "आवश्यक" नहीं हैं यदि लक्ष्य केवल एक एल्गोरिदम को लागू करना है। यदि लक्ष्य को पठनीय बनाए रखने योग्य कोड का तेजी से विकास करना है तो सबसे अधिक आवश्यक हैं लेकिन "पठनीय" और "बनाए रखने योग्य" औसत दर्जे का और अत्यंत व्यक्तिपरक नहीं हैं। अच्छी भाषा डेवलपर्स बहुत सारी विशेषताओं में डालते हैं: यदि आपके पास उनमें से कुछ के लिए उपयोग नहीं है तो उनका उपयोग न करें।
जेम्स एंडरसन

0

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

हालाँकि, मुझे अभी भी एक हल्की सुविधा मिलेगी यदि C ++ शामिल है finallyऔर ऐसा इसलिए है क्योंकि दो प्रकार के क्लीन-अप्स हैं:

  1. स्थानीय संसाधनों (विध्वंसक इसके लिए एकदम सही हैं) को नष्ट करना / मुक्त करना / अनलॉक करना / बंद करना आदि।
  2. बाहरी साइड इफेक्ट्स को नष्ट करना / रोल करना (इसके लिए विध्वंसक पर्याप्त हैं)।

दूसरा कम से कम संसाधन विनाश के विचार के लिए इतनी सहजता से मैप नहीं करता है, हालांकि आप इसे गुंजाइश गार्ड के साथ ठीक कर सकते हैं जो स्वचालित रूप से वापस परिवर्तन को रोल करते हैं जब वे प्रतिबद्ध होने से पहले नष्ट हो जाते हैं। वहाँ finallyयकीनन कम से कम एक प्रदान करता है थोड़ा गुंजाइश गार्ड से इस काम के लिए और अधिक सरल तंत्र (सिर्फ एक नन्हा सा द्वारा)।

हालाँकि, इससे भी अधिक सीधा तंत्र एक ऐसा rollbackब्लॉक होगा जो मैंने पहले कभी किसी भाषा में नहीं देखा है। यह मेरा एक पाइप का सपना है, अगर मैंने कभी ऐसी भाषा डिज़ाइन की है जिसमें अपवाद-हैंडलिंग शामिल है। यह इससे मिलता जुलता होगा:

try
{
    // Cause external side effects. These side effects should
    // be undone if we don't finish successfully.
}
rollback
{
    // Reverse external side effects. This block is *only* executed 
    // if the 'try' block above faced a premature return out 
    // of the function. It is different from 'finally' which 
    // gets executed regardless of whether or not the function 
    // exited prematurely. This block *only* gets executed if we 
    // exited prematurely from  the try block so that we can undo 
    // whatever side effects it failed to finish making. If the try 
    // block succeeded and didn't face a premature exit, then we 
    // don't want this block to execute.
}

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

ऐसे कुछ अस्पष्ट मामले भी हैं, जहाँ आप किसी भी कार्य को करना चाहते हैं, चाहे वह किसी कार्य से बाहर क्यों न हो, चाहे वह किसी समय टिकट पर लॉग इन कर रहा हो। वहाँ finallyयकीनन, इस काम के लिए सबसे सरल और सही समाधान है एक वस्तु का दृष्टांत के लिए केवल एक टाइमस्टैम्प प्रवेश करने का एकमात्र उद्देश्य के लिए अपने नाशक उपयोग करने की कोशिश के बाद से सिर्फ सच में अजीब (भले ही आप इसे ठीक कर सकते हैं लगता है और काफी आसानी से lambdas के साथ )।


-9

सी ++ भाषा के बारे में इतनी सारी अन्य असामान्य चीजों की तरह, एक try/finallyनिर्माण की कमी एक डिजाइन दोष है, अगर आप इसे एक भाषा में भी कह सकते हैं जो अक्सर प्रतीत होता है कि किसी भी वास्तविक डिजाइन का काम बिल्कुल नहीं हुआ है।

RAII (सफाई के लिए स्टैक-आधारित ऑब्जेक्ट पर स्कोप-आधारित निर्धारक डिस्ट्रॉक्टर इनवोकेशन का उपयोग) में दो गंभीर दोष हैं। पहला यह है कि इसमें स्टैक आधारित वस्तुओं के उपयोग की आवश्यकता होती है , जो कि एक घृणा है जो लिस्कोव प्रतिस्थापन सिद्धांत का उल्लंघन करती है। बहुत सारे अच्छे कारण हैं कि C ++ से पहले या बाद में किसी अन्य OO भाषा ने उनका उपयोग क्यों नहीं किया है - एप्सिलॉन के भीतर; D की गणना नहीं है क्योंकि यह C ++ पर बहुत अधिक आधारित है और इसका कोई बाज़ार हिस्सा नहीं है - और जो समस्याएँ हैं उन्हें स्पष्ट करना इस उत्तर के दायरे से बाहर है।

दूसरा, क्या finallyकर सकता है वस्तु विनाश का सुपरसेट। C ++ में RAII के साथ क्या किया जाता है, इसका वर्णन डेल्फी भाषा में किया जाएगा, जिसमें कचरा संग्रह नहीं है, निम्न पैटर्न के साथ:

myObject := MyClass.Create(arguments);
try
   doSomething(myObject);
finally
   myObject.Free();
end;

यह आरएआई पैटर्न स्पष्ट किया गया है; यदि आप एक C ++ रूटीन बनाते हैं जिसमें केवल पहली और तीसरी पंक्तियों के बराबर होता है, तो संकलक क्या उत्पन्न करेगा, यह देखने के समान होगा कि मैंने इसकी मूल संरचना में क्या लिखा है। और क्योंकि यह try/finallyC ++ प्रदान करने वाले निर्माण का एकमात्र उपयोग है , C ++ डेवलपर्स एक नहीं बल्कि बहुत ही निकट दृष्टि के साथ समाप्त होते हैं try/finally: जब आपके पास एक हथौड़ा होता है, तो सब कुछ एक विध्वंसक की तरह लगने लगता है, इसलिए बोलने के लिए।

लेकिन अन्य चीजें हैं जो एक अनुभवी डेवलपर एक finallyनिर्माण के साथ कर सकता है । यह निर्धारक विनाश के बारे में नहीं है, यहां तक ​​कि एक अपवाद के सामने भी; यह निर्धारक कोड निष्पादन के बारे में है , यहां तक ​​कि एक अपवाद के सामने भी।

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

dataset.DisableControls();
try
   LoadData(dataset);
finally
   dataset.EnableControls();
end;

स्पष्ट रूप से, यहां कोई वस्तु नष्ट नहीं हो रही है, और किसी की आवश्यकता नहीं है। कोड सरल, संक्षिप्त, स्पष्ट और कुशल है।

यह C ++ में कैसे किया जाएगा? ठीक है, पहले आपको एक पूरी कक्षा को कोड करना होगा । यह शायद कहा जाएगा DatasetEnablerया सोमसुख बात। इसका पूरा अस्तित्व RAII के सहायक के रूप में होगा। फिर आपको ऐसा कुछ करने की आवश्यकता होगी:

dataset.DisableControls();
{
   raiiGuard = DatasetEnabler(dataset);
   LoadData(dataset);
}

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

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


टिप्पणियाँ एक प्रश्न और उत्तर को स्पष्ट करने या सुधारने के लिए होती हैं। यदि आप इस उत्तर के बारे में चर्चा करना चाहते हैं तो कृपया चैट रूम में जाएं। धन्यवाद।
maple_shaft
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.