क्या C ++ 'अंततः' ब्लॉक का समर्थन करता है? (और यह 'RAII' क्या है जिसके बारे में मैं सुनता रहता हूँ?)


272

क्या C ++ ' अंततः ' ब्लॉक का समर्थन करता है ?

RAII मुहावरा क्या है ?

C ++ के RAII मुहावरे और C # 's' कथन के बीच अंतर क्या है ?


1
इसके उत्तर भी देखें: stackoverflow.com/questions/7779652/…
18446744073709551615

जवाबों:


273

नहीं, C ++ 'अंततः' ब्लॉक का समर्थन नहीं करता है। "संसाधन अधिग्रहण है प्रारंभ" - एक: कारण यह है कि सी ++ के बजाय आरए II का समर्थन करता है है गरीब नाम एक बहुत उपयोगी अवधारणा के लिए।

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

RAII के लिए एक सामान्य उपयोग म्यूटेक्स को लॉक कर रहा है:

// A class with implements RAII
class lock
{
    mutex &m_;

public:
    lock(mutex &m)
      : m_(m)
    {
        m.acquire();
    }
    ~lock()
    {
        m_.release();
    }
};

// A class which uses 'mutex' and 'lock' objects
class foo
{
    mutex mutex_; // mutex for locking 'foo' object
public:
    void bar()
    {
        lock scopeLock(mutex_); // lock object.

        foobar(); // an operation which may throw an exception

        // scopeLock will be destructed even if an exception
        // occurs, which will release the mutex and allow
        // other functions to lock the object and run.
    }
};

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

C # या VB.NET वाले उन पारिवारिक लोगों के लिए, आप पहचान सकते हैं कि RAII .NET नियतांक के रूप में आईडीआईएसओपी और 'स्टेटमेंट' का उपयोग करके विनाशकारी विनाश के समान है । वास्तव में, दो विधियां बहुत समान हैं। मुख्य अंतर यह है कि RAII किसी भी प्रकार के संसाधन को निर्धारित करेगा - जिसमें मेमोरी भी शामिल है। .NET (यहां तक ​​कि .NET लैंग्वेज C ++ / CLI) में भी आईडीआईसपोजेबल लागू करते समय, संसाधनों को मेमोरी के अलावा निर्दिष्‍ट रूप से जारी किया जाएगा। .NET में, मेमोरी नियत रूप से जारी नहीं की जाती है; मेमोरी केवल कचरा संग्रह चक्र के दौरान जारी की जाती है।

 

† कुछ लोगों का मानना ​​है कि "विनाश संसाधन संसाधन है" RAII मुहावरे का अधिक सटीक नाम है।


18
"विनाश संसाधन पुनर्स्थापन है" - DIRR ... नहींं, मेरे लिए काम नहीं करता है। = पी
एरिक फोर्ब्स

14
RAII फंस गया है - वास्तव में इसमें कोई बदलाव नहीं हुआ है। ऐसा करने की कोशिश करना मूर्खतापूर्ण होगा। हालाँकि, आपको यह स्वीकार करना होगा कि "संसाधन अधिग्रहण इज इनिशियलाइज़ेशन" अभी भी एक बहुत खराब नाम है।
केविन

162
एसबीआरएम == स्कोप बाउंड रिसोर्स मैनेजमेंट
जोहान्स

10
इंजीनियर के कौशल के साथ कोई भी न केवल सॉफ्टवेयर आमतौर पर, अकेले बेहतर तकनीक देता है, इस तरह के एक भयावह परिचित के लिए कोई योग्य बहाना नहीं दे सकता है।
२०:

54
जब आप किसी चीज को साफ करने के लिए कुछ सी ++ वस्तु के जीवनकाल से मेल नहीं खाते हैं तो यह आपको छोड़ देता है। मुझे लगता है कि आप लाइफटाइम बराबर C ++ क्लास लिफ़्टाइम या एल्स के साथ समाप्त हो जाते हैं यह बदसूरत (LECCLEEEIGU?) है।
वारेन P

79

C ++ में RAII के कारण अंत में आवश्यक नहीं है।

RAII ऑब्जेक्ट के उपयोगकर्ता से ऑब्जेक्ट के डिज़ाइनर (और कार्यान्वयनकर्ता) तक अपवाद सुरक्षा की जिम्मेदारी ले जाता है। मेरा तर्क है कि यह सही जगह है क्योंकि आपको केवल एक बार (डिजाइन / कार्यान्वयन में) अपवाद सुरक्षा को सही करने की आवश्यकता है। अंत में किसी वस्तु का उपयोग करने पर आपको हर बार अपवाद सुरक्षा सही प्राप्त करने की आवश्यकता होती है।

इसके अलावा IMO कोड निचे दिखता है (नीचे देखें)।

उदाहरण:

एक डेटाबेस ऑब्जेक्ट। यह सुनिश्चित करने के लिए कि DB कनेक्शन का उपयोग किया जाता है, इसे खोला और बंद किया जाना चाहिए। RAII का उपयोग करके इसे कंस्ट्रक्टर / डिस्ट्रक्टर में किया जा सकता है।

C ++ RAII की तरह

void someFunc()
{
    DB    db("DBDesciptionString");
    // Use the db object.

} // db goes out of scope and destructor closes the connection.
  // This happens even in the presence of exceptions.

RAII का उपयोग किसी DB ऑब्जेक्ट का सही ढंग से उपयोग करना बहुत आसान बनाता है। डीबी ऑब्जेक्ट सही ढंग से एक विध्वंसक के उपयोग से खुद को बंद कर देगा चाहे हम इसे कैसे भी प्रयास करें और दुरुपयोग करें।

जावा लाइक आखिर

void someFunc()
{
    DB      db = new DB("DBDesciptionString");
    try
    {
        // Use the db object.
    }
    finally
    {
        // Can not rely on finaliser.
        // So we must explicitly close the connection.
        try
        {
            db.close();
        }
        catch(Throwable e)
        {
           /* Ignore */
           // Make sure not to throw exception if one is already propagating.
        }
    }
}

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

साथ ही यह एक सरल उदाहरण है।
जब आपके पास कई संसाधन हैं जिन्हें जारी करने की आवश्यकता है तो कोड जटिल हो सकता है।

अधिक विस्तृत विश्लेषण यहां पाया जा सकता है: http://accu.org/index.php/journals/236


16
// Make sure not to throw exception if one is already propagating.C ++ डिस्ट्रक्टर्स के लिए अपवादों को फेंकना महत्वपूर्ण है और साथ ही इस कारण से महत्वपूर्ण है।
सीमाफोर

10
@ कैमाफोर: सी ++ के कारण विध्वंसक के अपवादों को न फेंकने का कारण जावा से अलग है। जावा में यह काम करेगा (आप मूल अपवाद को ढीला कर दें)। C ++ में इसका वास्तव में बुरा है। लेकिन C ++ में बिंदु यह है कि आपको इसे केवल एक बार करना होगा (कक्षा के डिजाइनर द्वारा) जब वह विध्वंसक लिखता है। जावा में आपको इसे उपयोग के बिंदु पर करना होगा। तो यह वर्ग के उपयोगकर्ता की ज़िम्मेदारी है कि वह एक ही बॉयलर प्लेट को बहुत बार लिखे।
मार्टिन यॉर्क

1
यदि यह "आवश्यक" होने की बात है, तो आपको RAII की आवश्यकता नहीं है। चलो इसे से छुटकारा! :-) एक तरफ चुटकुले, RAII बहुत मामलों के लिए ठीक है। जब RAII अधिक बोझिल बनाता है तो ऐसे मामले होते हैं जब आप कुछ कोड (संसाधन संबंधी नहीं) निष्पादित करना चाहते हैं, भले ही ऊपर वाला कोड जल्दी लौट आए। उसके लिए या तो आप गोटो का उपयोग करें या इसे दो तरीकों से अलग करें।
त्रिनिदाद

1
@ ट्रिनिडाड: यह एक सरल नहीं है जैसा कि आप सोचते हैं (जैसा कि आपके सभी सुझाव संभव सबसे खराब विकल्प चुनना चाहते हैं)। यही कारण है कि एक टिप्पणी टिप्पणियों की तुलना में इसका पता लगाने के लिए एक बेहतर जगह हो सकती है।
मार्टिन यॉर्क

1
"RAII के कारण आवश्यक नहीं है" की आलोचना करना: ऐसे बहुत से मामले हैं जहां ऐड-हॉक RAII को जोड़ना बहुत अधिक बॉयलरप्लेट कोड जोड़ना होगा, और कोशिश करना अंत में बस बेहद उपयुक्त होगा।
19

63

आरएआई आमतौर पर बेहतर होता है, लेकिन आप आसानी से सी ++ में अंतिम शब्दार्थ को आसानी से प्राप्त कर सकते हैं । कोड की एक छोटी राशि का उपयोग करना।

इसके अलावा, C ++ कोर दिशानिर्देश अंत में देते हैं।

यहाँ जीएसएल Microsoft कार्यान्वयन और मार्टिन मोइने कार्यान्वयन के लिए एक कड़ी है

बज़्ने स्ट्रॉस्ट्रुप ने कई बार कहा कि जीएसएल में वह सब कुछ है जो अंततः मानक में जाने का था। तो यह अंत में उपयोग करने के लिए एक भविष्य प्रूफ तरीका होना चाहिए ।

आप आसानी से अपने आप को लागू कर सकते हैं यदि आप चाहें, तो पढ़ना जारी रखें।

C ++ 11 RAII और लैम्ब्डा में अंत में एक सामान्य बनाने की अनुमति देता है:

namespace detail { //adapt to your "private" namespace
template <typename F>
struct FinalAction {
    FinalAction(F f) : clean_{f} {}
   ~FinalAction() { if(enabled_) clean_(); }
    void disable() { enabled_ = false; };
  private:
    F clean_;
    bool enabled_{true}; }; }

template <typename F>
detail::FinalAction<F> finally(F f) {
    return detail::FinalAction<F>(f); }

उपयोग का उदाहरण:

#include <iostream>
int main() {
    int* a = new int;
    auto delete_a = finally([a] { delete a; std::cout << "leaving the block, deleting a!\n"; });
    std::cout << "doing something ...\n"; }

उत्पादन होगा:

doing something...
leaving the block, deleting a!

व्यक्तिगत रूप से मैंने C ++ प्रोग्राम में POSIX फाइल डिस्क्रिप्टर को बंद करने के लिए कुछ समय का उपयोग किया।

एक वास्तविक वर्ग का होना जो संसाधनों का प्रबंधन करता है और इसलिए किसी भी तरह के लीक से बचा जाता है, आमतौर पर बेहतर होता है, लेकिन यह अंत में उन मामलों में उपयोगी होता है, जहां कक्षा को ओवरकिल की तरह लगता है।

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

एक और उदाहरण:

 [...]
 auto precision = std::cout.precision();
 auto set_precision_back = finally( [precision, &std::cout]() { std::cout << std::setprecision(precision); } );
 std::cout << std::setprecision(3);

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

अक्षम उदाहरण:

//strong guarantee
void copy_to_all(BIGobj const& a) {
    first_.push_back(a);
    auto undo_first_push = finally([first_&] { first_.pop_back(); });

    second_.push_back(a);
    auto undo_second_push = finally([second_&] { second_.pop_back(); });

    third_.push_back(a);
    //no necessary, put just to make easier to add containers in the future
    auto undo_third_push = finally([third_&] { third_.pop_back(); });

    undo_first_push.disable();
    undo_second_push.disable();
    undo_third_push.disable(); }

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

#include <iostream>
int main() {
    int* a = new int;

    struct Delete_a_t {
        Delete_a_t(int* p) : p_(p) {}
       ~Delete_a_t() { delete p_; std::cout << "leaving the block, deleting a!\n"; }
        int* p_;
    } delete_a(a);

    std::cout << "doing something ...\n"; }

एक संभावित समस्या हो सकती है: फ़ंक्शन 'अंततः (F f)' में यह फ़ाइनलएक्शन का ऑब्जेक्ट देता है, इसलिए अंत में फ़ंक्शन के लौटने से पहले डिकंस्ट्रक्टर को बुलाया जा सकता है। शायद हमें टेम्पलेट F के बजाय std :: function का उपयोग करना चाहिए
user1633272

ध्यान दें कि FinalActionमूल रूप से लोकप्रिय ScopeGuardमुहावरे के रूप में ही है , केवल एक अलग नाम के साथ।
औरसीस

1
क्या यह अनुकूलन सुरक्षित है?
नूलानो

2
@ Paolo.Bolzoni जल्दी जवाब न देने के लिए क्षमा करें, मुझे आपकी टिप्पणी के लिए सूचना नहीं मिली। मुझे चिंता थी कि आखिरकार ब्लॉक (जिसमें मैं DLL फ़ंक्शन कहता हूं) को स्कोप के अंत से पहले बुलाया जाएगा (क्योंकि चर अप्रयुक्त है), लेकिन तब से SO पर एक प्रश्न मिला है जिसने मेरी चिंताओं को दूर किया है। मैं इसे लिंक करूंगा, लेकिन दुर्भाग्य से, मैं इसे अब और नहीं ढूंढ सकता।
नूलानो

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

32

स्टैक-आधारित वस्तुओं के साथ सफाई को आसान बनाने से परे, RAII भी उपयोगी है क्योंकि एक ही 'स्वचालित' सफाई तब होती है जब ऑब्जेक्ट किसी अन्य वर्ग का सदस्य होता है। जब मालिक वर्ग को नष्ट कर दिया जाता है, तो RAII वर्ग द्वारा प्रबंधित संसाधन साफ ​​हो जाता है क्योंकि उस वर्ग के लिए dtor को परिणाम के रूप में कहा जाता है।

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


यह बहुत अच्छी बात है। आप को +1। हालांकि कई अन्य लोगों ने आपको वोट नहीं दिया है। मुझे आशा है कि आपको कोई आपत्ति नहीं है कि मैंने अपनी टिप्पणी शामिल करने के लिए अपनी पोस्ट को संपादित किया। (मैंने आपको पाठ्यक्रम का श्रेय दिया।) धन्यवाद! :)
केविन

30

ऐसा क्यों है कि प्रबंधित भाषाएं संसाधनों को स्वचालित रूप से कचरे के संग्रहकर्ता द्वारा वैसे भी ख़त्म किए जाने के बावजूद आखिरकार ब्लॉक प्रदान करती हैं?

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

गतिशील रूप से आवंटित डेटा के संदर्भ में, कई तर्क देंगे कि आपको स्मार्ट-पॉइंटर्स का उपयोग करना चाहिए।

तथापि...

RAII ऑब्जेक्ट के उपयोगकर्ता से डिजाइनर तक अपवाद सुरक्षा की जिम्मेदारी ले जाता है

अफसोस की बात है कि यह अपनी गिरावट है। पुरानी सी प्रोग्रामिंग आदतें कठिन मर जाती हैं। जब आप C या बहुत C शैली में लिखी गई लाइब्रेरी का उपयोग कर रहे हों, तो RAII का उपयोग नहीं किया जाएगा। संपूर्ण एपीआई फ्रंट-एंड को फिर से लिखने की कमी, बस यही आपके साथ काम करना है। फिर "अंत" की कमी वास्तव में काटती है।


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

7
@JamesJohnston, रैपर क्लास लिखना बहुत आसान है जो किसी भी तरह का हैंडल रखता है और RAII मैकेनिक्स प्रदान करता है। ATL उदाहरण के लिए उनमें से एक गुच्छा प्रदान करता है। ऐसा लगता है कि आप इसे बहुत अधिक परेशानी मानते हैं लेकिन मैं असहमत हूं, वे बहुत छोटे हैं और लिखना आसान है।
मार्क रैनसम

5
सरल हाँ, छोटा नहीं। आकार उस लाइब्रेरी की जटिलता पर निर्भर करता है जिसमें आप काम कर रहे हैं।
फिलिप कूपलिंग

1
@MarkRansom: क्या कोई ऐसा तंत्र है जिसके माध्यम से RAII सफाई के दौरान कुछ अपवाद कर सकता है जबकि कोई अन्य अपवाद लंबित है? कोशिशों के साथ सिस्टम में / आखिरकार, यह संभव है - हालांकि अजीब है - चीजों को व्यवस्थित करने के लिए ताकि सफाई के दौरान लंबित अपवाद और अपवाद दोनों एक नए में संग्रहीत हो जाएं CleanupFailedException। क्या RAII का उपयोग करके इस तरह के परिणाम को प्राप्त करने का कोई प्रशंसनीय तरीका है?
सुपरकैट

3
@couling: ऐसे कई मामले हैं जहां कोई प्रोग्राम एक SomeObject.DoSomething()विधि को कॉल करेगा और जानना चाहेगा कि क्या यह (1) सफल हुआ, (2) कोई साइड-इफेक्ट्स के साथ विफल रहा, (3) साइड-इफेक्ट्स के साथ असफल कॉल करने वाले से निपटने के लिए तैयार है , या (4) साइड-इफ़ेक्ट्स के साथ विफल हो गया, जिससे कॉलर सामना नहीं कर सकता। केवल फोन करने वाले को यह पता चल जाता है कि वह किन परिस्थितियों में सामना कर सकता है; कॉलर को क्या चाहिए, यह जानने का एक तरीका है कि स्थिति क्या है। यह बहुत बुरा है कि अपवाद के बारे में सबसे महत्वपूर्ण जानकारी की आपूर्ति के लिए कोई मानक तंत्र नहीं है।
Supercat

9

C ++ 11 लैम्ब्डा फ़ंक्शन का उपयोग करके एक और "अंततः" ब्लॉक इम्यूलेशन

template <typename TCode, typename TFinallyCode>
inline void with_finally(const TCode &code, const TFinallyCode &finally_code)
{
    try
    {
        code();
    }
    catch (...)
    {
        try
        {
            finally_code();
        }
        catch (...) // Maybe stupid check that finally_code mustn't throw.
        {
            std::terminate();
        }
        throw;
    }
    finally_code();
}

चलो उम्मीद करते हैं कि संकलक ऊपर दिए गए कोड का अनुकूलन करेगा।

अब हम इस तरह कोड लिख सकते हैं:

with_finally(
    [&]()
    {
        try
        {
            // Doing some stuff that may throw an exception
        }
        catch (const exception1 &)
        {
            // Handling first class of exceptions
        }
        catch (const exception2 &)
        {
            // Handling another class of exceptions
        }
        // Some classes of exceptions can be still unhandled
    },
    [&]() // finally
    {
        // This code will be executed in all three cases:
        //   1) exception was not thrown at all
        //   2) exception was handled by one of the "catch" blocks above
        //   3) exception was not handled by any of the "catch" block above
    }
);

यदि आप चाहें तो इस मुहावरे को "अंतत:" मैक्रोज़ में लपेट सकते हैं:

// Please never throw exception below. It is needed to avoid a compilation error
// in the case when we use "begin_try ... finally" without any "catch" block.
class never_thrown_exception {};

#define begin_try    with_finally([&](){ try
#define finally      catch(never_thrown_exception){throw;} },[&]()
#define end_try      ) // sorry for "pascalish" style :(

अब "अंततः" ब्लॉक C ++ 11 में उपलब्ध है:

begin_try
{
    // A code that may throw
}
catch (const some_exception &)
{
    // Handling some exceptions
}
finally
{
    // A code that is always executed
}
end_try; // Sorry again for this ugly thing

व्यक्तिगत रूप से मुझे "अंततः" मुहावरे के "मैक्रो" संस्करण पसंद नहीं है और उस मामले में एक सिंटैक्स अधिक भारी होने के बावजूद शुद्ध "with_finally" फ़ंक्शन का उपयोग करना पसंद करेंगे।

आप ऊपर दिए गए कोड का परीक्षण यहां कर सकते हैं: http://coliru.stacked-crooked.com/a/1d88f64cb2783813

पुनश्च

यदि आपको अपने कोड में अंत में ब्लॉक की आवश्यकता है , तो स्कॉप्ड गार्ड या ON_FINALLY / ON_EXCEPTION मैक्रोज़ शायद आपकी आवश्यकताओं को बेहतर रूप से फिट करेंगे।

यहाँ ON_FINALLY / ON_EXCEPTION के उपयोग का संक्षिप्त उदाहरण है:

void function(std::vector<const char*> &vector)
{
    int *arr1 = (int*)malloc(800*sizeof(int));
    if (!arr1) { throw "cannot malloc arr1"; }
    ON_FINALLY({ free(arr1); });

    int *arr2 = (int*)malloc(900*sizeof(int));
    if (!arr2) { throw "cannot malloc arr2"; }
    ON_FINALLY({ free(arr2); });

    vector.push_back("good");
    ON_EXCEPTION({ vector.pop_back(); });

    ...

1
इस पृष्ठ में प्रस्तुत सभी विकल्पों में से सबसे अधिक पठनीय मेरे लिए पहला है। +1
निकोस

7

इस तरह के एक पुराने धागे को खोदने के लिए क्षमा करें, लेकिन निम्नलिखित तर्क में एक बड़ी त्रुटि है:

RAII ऑब्जेक्ट के उपयोगकर्ता से ऑब्जेक्ट के डिज़ाइनर (और कार्यान्वयनकर्ता) तक अपवाद सुरक्षा की जिम्मेदारी ले जाता है। मेरा तर्क है कि यह सही जगह है क्योंकि आपको केवल एक बार (डिजाइन / कार्यान्वयन में) अपवाद सुरक्षा को सही करने की आवश्यकता है। अंत में किसी वस्तु का उपयोग करने पर आपको हर बार अपवाद सुरक्षा सही प्राप्त करने की आवश्यकता होती है।

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

void DoStuff(vector<string> input)
{
  list<Foo*> myList;

  try
  {    
    for (int i = 0; i < input.size(); ++i)
    {
      Foo* tmp = new Foo(input[i]);
      if (!tmp)
        throw;

      myList.push_back(tmp);
    }

    DoSomeStuff(myList);
  }
  finally
  {
    while (!myList.empty())
    {
      delete myList.back();
      myList.pop_back();
    }
  }
}

बेशक सूची दायरे से बाहर जाने पर ही नष्ट हो जाएगी, लेकिन इससे आपके द्वारा बनाई गई अस्थायी वस्तुओं की सफाई नहीं होगी।

इसके बजाय, आपको बदसूरत मार्ग पर जाना होगा:

void DoStuff(vector<string> input)
{
  list<Foo*> myList;

  try
  {    
    for (int i = 0; i < input.size(); ++i)
    {
      Foo* tmp = new Foo(input[i]);
      if (!tmp)
        throw;

      myList.push_back(tmp);
    }

    DoSomeStuff(myList);
  }
  catch(...)
  {
  }

  while (!myList.empty())
  {
    delete myList.back();
    myList.pop_back();
  }
}

इसके अलावा: ऐसा क्यों है कि प्रबंधित लानूज़, संसाधनों को स्वचालित रूप से कचरा संग्रहकर्ता द्वारा वैसे भी ख़त्म किए जाने के बावजूद आखिरकार ब्लॉक प्रदान करते हैं?

सुझाव: वहाँ अधिक है कि आप "मेमोरी" की तुलना में "अंत में" कर सकते हैं।


17
प्रबंधित भाषाओं को अंततः-ब्लॉक की आवश्यकता होती है क्योंकि केवल एक प्रकार का संसाधन स्वचालित रूप से प्रबंधित होता है: मेमोरी। RAII का अर्थ है कि सभी संसाधनों को एक ही तरह से संभाला जा सकता है, इसलिए अंत में इसकी कोई आवश्यकता नहीं है। यदि आप वास्तव में अपने उदाहरण में RAII का उपयोग करते हैं (नग्न लोगों के बजाय अपनी सूची में स्मार्ट पॉइंटर्स का उपयोग करके), तो कोड आपके "अंततः" -example से अधिक सरल होगा। और भी सरल अगर आप नए की वापसी मूल्य की जांच नहीं करते हैं - यह जाँच बहुत बेकार है।
मायटो

7
newNULL को नहीं लौटाता, इसके बजाय एक अपवाद फेंकता है
Hasturkun

5
आप एक महत्वपूर्ण प्रश्न उठाते हैं, लेकिन इसके 2 संभावित उत्तर हैं। एक यह है कि Myto द्वारा दिया गया है - सभी गतिशील आवंटन के लिए स्मार्ट पॉइंटर्स का उपयोग करें। अन्य मानक कंटेनरों का उपयोग करना है, जो हमेशा विनाश पर अपनी सामग्री को नष्ट करते हैं। किसी भी तरह से, हर आवंटित वस्तु अंततः एक आबंटित वस्तु के स्वामित्व में होती है जो इसे नष्ट होने पर स्वत: मुक्त कर देती है। यह एक वास्तविक शर्म की बात है कि ये बेहतर समाधान प्रोग्रामर के लिए कठिन हैं क्योंकि वे सादे बिंदुओं और सरणियों की उच्च दृश्यता की खोज करते हैं।
j_random_hacker

4
C ++ 11 इसमें सुधार करता है और इसमें सीधे std::shared_ptrऔर std::unique_ptrstdlib शामिल हैं।
u0b34a0f6ae

16
आपका उदाहरण इतना भयानक लगने का कारण यह नहीं है कि RAII त्रुटिपूर्ण है, बल्कि ऐसा इसलिए है क्योंकि आप इसका उपयोग करने में विफल रहे हैं। कच्चे संकेत RAII नहीं हैं।
बेन वोइग्ट

6

FWIW, Microsoft Visual C ++ समर्थन का प्रयास करता है, आखिरकार और इसे ऐतिहासिक रूप से MFC ऐप में गंभीर अपवादों को पकड़ने के एक तरीके के रूप में उपयोग किया गया है जो अन्यथा दुर्घटना का परिणाम होगा। उदाहरण के लिए;

int CMyApp::Run() 
{
    __try
    {
        int i = CWinApp::Run();
        m_Exitok = MAGIC_EXIT_NO;
        return i;
    }
    __finally
    {
        if (m_Exitok != MAGIC_EXIT_NO)
            FaultHandler();
    }
}

मैंने अतीत में इसका उपयोग बाहर निकलने से पहले खुली फाइलों के बैकअप को बचाने जैसे कामों के लिए किया है। कुछ JIT डिबगिंग सेटिंग्स इस तंत्र को तोड़ देंगी।


4
ध्यान रखें कि वास्तव में C ++ अपवाद नहीं है, लेकिन SEH वाले हैं। आप MS C ++ कोड में दोनों का उपयोग कर सकते हैं। SEH एक OS अपवाद हैंडलर है जो VB, .NET कार्यान्वयन अपवाद है।
gbjbaanb

और आप SEU अपवादों के लिए 'Global' संयुक्त राष्ट्र के कैच अपवाद हैंडलर बनाने के लिए SetUnhandledExceptionHandler का उपयोग कर सकते हैं।
gbjbaanb

3
SEH भयानक है और C ++ डिस्ट्रक्टर्स को भी बुलाया जा रहा है
पॉल

6

जैसा कि अन्य उत्तरों में बताया गया है, C ++ समान finally-कार्यक्षमता का समर्थन कर सकता है । मानक भाषा का हिस्सा होने के लिए संभवतः निकटतम इस कार्यक्षमता का कार्यान्वयन C ++ कोर दिशानिर्देशों में से एक है, Bjarne स्टॉस्ट्रुप और हर्ब सटर द्वारा संपादित C ++ का उपयोग करने के लिए सर्वोत्तम प्रथाओं का एक सेट। इसका कार्यान्वयन finallyदिशानिर्देश सहायता लाइब्रेरी (GSL) का एक हिस्सा है । दिशानिर्देशों के दौरान, finallyपुरानी शैली के इंटरफेस से निपटने के दौरान उपयोग की सिफारिश की जाती है, और इसकी स्वयं की एक दिशानिर्देश भी है, जिसका शीर्षक है क्लीनअप को व्यक्त करने के लिए एक अंतिम_ उपयोग वस्तु यदि कोई उपयुक्त संसाधन संभाल उपलब्ध नहीं है

इसलिए, न केवल सी ++ का समर्थन करता है finally, वास्तव में इसे बहुत सारे सामान्य उपयोग के मामलों में उपयोग करने की सिफारिश की जाती है।

जीएसएल कार्यान्वयन का एक उदाहरण उपयोग इस तरह दिखेगा:

#include <gsl/gsl_util.h>

void example()
{
    int handle = get_some_resource();
    auto handle_clean = gsl::finally([&handle] { clean_that_resource(handle); });

    // Do a lot of stuff, return early and throw exceptions.
    // clean_that_resource will always get called.
}

GSL कार्यान्वयन और उपयोग पाओलो.बोलज़ोनी के उत्तर के समान है । एक अंतर यह है कि कॉल द्वारा बनाई गई वस्तु की gsl::finally()कमी है disable()। यदि आपको उस कार्यक्षमता की आवश्यकता है (कहते हैं, एक बार इकट्ठा होने के बाद संसाधन को वापस करने के लिए और कोई अपवाद होने के लिए बाध्य नहीं है), तो आप पाओलो के कार्यान्वयन को पसंद कर सकते हैं। अन्यथा, जीएसएल का उपयोग करना मानकीकृत सुविधाओं का उपयोग करने के करीब है जितना आपको मिलेगा।


3

वास्तव में नहीं, लेकिन आप उन्हें कुछ विस्तार के लिए अनुकरण कर सकते हैं, उदाहरण के लिए:

int * array = new int[10000000];
try {
  // Some code that can throw exceptions
  // ...
  throw std::exception();
  // ...
} catch (...) {
  // The finally-block (if an exception is thrown)
  delete[] array;
  // re-throw the exception.
  throw; 
}
// The finally-block (if no exception was thrown)
delete[] array;

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


3
मुझे खुशी है कि आपने उल्लेख किया कि अंततः ब्लॉक फेंक सकते हैं; यह वह चीज है जिसका ज्यादातर "आरएआईआई उपयोग करते हैं" जवाब नजरअंदाज करते हैं। दो बार अंत में ब्लॉक लिखने से बचने के लिए, आप कुछ ऐसा कर सकते हैंstd::exception_ptr e; try { /*try block*/ } catch (...) { e = std::current_exception(); } /*finally block*/ if (e) std::rethrow_exception(e);
सेथोब्रिएन

1
यही सब मैं जानना चाहता था! अन्य उत्तरों में से किसी ने भी क्यों नहीं समझाया कि एक पकड़ (...) + खाली फेंक; लगभग अंत में ब्लॉक की तरह काम करता है? कभी-कभी आपको बस जरूरत होती है।
विनगरिया जूल

मैंने अपने उत्तर में जो समाधान दिया है ( stackoverflow.com/a/38701485/566849 ) को finallyब्लॉक के अंदर से अपवाद फेंकने की अनुमति देनी चाहिए ।
फैबियो ए।

3

मैं एक के साथ आया था finallyमैक्रो इस्तेमाल किया जा सकता है कि लगभग की तरह ¹ finallyजावा में कीवर्ड; यह std::exception_ptrऔर दोस्तों, लैम्ब्डा कार्यों और का उपयोग करता है std::promise, इसलिए इसकी आवश्यकता होती है C++11या ऊपर; यह यौगिक कथन अभिव्यक्ति का उपयोग भी करता है GCC एक्सटेंशन , जो क्लैंग द्वारा समर्थित है।

चेतावनी : इस उत्तर के एक पुराने संस्करण ने कई और सीमाओं के साथ अवधारणा के एक अलग कार्यान्वयन का उपयोग किया।

सबसे पहले, एक सहायक वर्ग को परिभाषित करते हैं।

#include <future>

template <typename Fun>
class FinallyHelper {
    template <typename T> struct TypeWrapper {};
    using Return = typename std::result_of<Fun()>::type;

public:    
    FinallyHelper(Fun body) {
        try {
            execute(TypeWrapper<Return>(), body);
        }
        catch(...) {
            m_promise.set_exception(std::current_exception());
        }
    }

    Return get() {
        return m_promise.get_future().get();
    }

private:
    template <typename T>
    void execute(T, Fun body) {
        m_promise.set_value(body());
    }

    void execute(TypeWrapper<void>, Fun body) {
        body();
    }

    std::promise<Return> m_promise;
};

template <typename Fun>
FinallyHelper<Fun> make_finally_helper(Fun body) {
    return FinallyHelper<Fun>(body);
}

फिर वास्तविक मैक्रो है।

#define try_with_finally for(auto __finally_helper = make_finally_helper([&] { try 
#define finally });                         \
        true;                               \
        ({return __finally_helper.get();})) \
/***/

इसका उपयोग इस तरह किया जा सकता है:

void test() {
    try_with_finally {
        raise_exception();
    }    

    catch(const my_exception1&) {
        /*...*/
    }

    catch(const my_exception2&) {
        /*...*/
    }

    finally {
        clean_it_all_up();
    }    
}

के उपयोग std::promiseबनाता है यह बहुत आसान लागू करने के लिए, लेकिन यह शायद भी काफी अनावश्यक भूमि के ऊपर का एक सा है जहाँ से आप केवल जरूरत कार्यक्षमताओं reimplementing से बचा जा सकता है का परिचय std::promise


¹ चेतावनी: वहाँ कुछ चीजें हैं जो काफी के जावा संस्करण की तरह काम नहीं करते हैं finally। मेरे सर के ऊपर से चला गया:

  1. इसके साथ एक बाहरी पाश से तोड़ने के लिए संभव नहीं है breakके भीतर से बयान tryऔर catch(), के ब्लॉक क्योंकि वे एक लैम्ब्डा समारोह के भीतर रहते हैं;
  2. के catch()बाद कम से कम एक ब्लॉक होना चाहिए try: यह एक सी ++ आवश्यकता है;
  3. यदि फ़ंक्शन में शून्य के अलावा कोई वापसी मान है, लेकिन tryऔर catch()'sब्लॉक के भीतर कोई रिटर्न नहीं है , तो संकलन विफल हो जाएगा क्योंकि finallyमैक्रो कोड में विस्तारित हो जाएगा जो वापस लौटना चाहेगा void। यह, एक प्रकार का मैक्रो होने के द्वारा , एक शून्य संस्करण हो सकता है finally_noreturn

सब सब में, मुझे नहीं पता कि क्या मैं कभी भी इस सामान का उपयोग करूँगा, लेकिन इसके साथ खेलने में मज़ा आया। :)


हाँ, यह सिर्फ एक तेज़ हैक था, लेकिन अगर प्रोग्रामर जानता है कि वे क्या कर रहे हैं तो यह उपयोगी हो सकता है।
Fabio A.

@MarkLakata, मैंने एक बेहतर कार्यान्वयन के साथ पोस्ट को अपडेट किया जो अपवादों और रिटर्न को फेंकने का समर्थन करता है।
फैबियो ए।

अछा लगता है। आप मैक्रों catch(xxx) {}की शुरुआत में एक असंभव ब्लॉक में डालकर कैविएट 2 से छुटकारा पा सकते हैं finally, जहां कम से कम एक कैच ब्लॉक होने के प्रयोजनों के लिए xxx एक फर्जी प्रकार है।
मार्क लकाटा

@MarkLakata, मैंने भी ऐसा ही सोचा था, लेकिन इसका उपयोग करना असंभव catch(...)होगा, है ना?
फेबियो ए।

मुझे ऐसा नहीं लगता। बस xxxएक निजी नाम स्थान में एक अस्पष्ट प्रकार बनाते हैं जिसका उपयोग कभी नहीं किया जाएगा।
मार्क लकाटा

2

मेरे पास एक उपयोग का मामला है जहां मुझे लगता है कि सी ++ 11 भाषा का एक बिल्कुल स्वीकार्य हिस्सा finally होना चाहिए , क्योंकि मुझे लगता है कि प्रवाह के दृष्टिकोण से पढ़ना आसान है। मेरे उपयोग का मामला एक उपभोक्ता / निर्माता श्रृंखला है, जहां एक प्रहरीnullptr सभी थ्रेड्स को बंद करने के लिए रन के अंत में भेजा जाता है।

यदि C ++ ने इसका समर्थन किया है, तो आप चाहेंगे कि आपका कोड इस तरह दिखाई दे:

    extern Queue downstream, upstream;

    int Example()
    {
        try
        {
           while(!ExitRequested())
           {
             X* x = upstream.pop();
             if (!x) break;
             x->doSomething();
             downstream.push(x);
           } 
        }
        finally { 
            downstream.push(nullptr);
        }
    }

मुझे लगता है कि यह अधिक तार्किक है कि लूप की शुरुआत में अपनी अंतिम घोषणा को लागू करना, क्योंकि यह लूप के बाहर निकलने के बाद होता है ... लेकिन यह इच्छाधारी सोच है क्योंकि हम इसे सी ++ में नहीं कर सकते। ध्यान दें कि कतार downstreamदूसरे धागे से जुड़ी है, इसलिए आप प्रहरी push(nullptr)में नहीं डाल सकते downstreamक्योंकि यह इस बिंदु पर नष्ट नहीं हो सकता ... इसे तब तक जीवित रहने की आवश्यकता है जब तक कि दूसरा धागा प्राप्त न हो जाए nullptr

तो यहाँ है कि लैम्ब्डा के साथ RAII वर्ग का उपयोग कैसे किया जाए:

    class Finally
    {
    public:

        Finally(std::function<void(void)> callback) : callback_(callback)
        {
        }
        ~Finally()
        {
            callback_();
        }
        std::function<void(void)> callback_;
    };

और यहां बताया गया है कि आप इसका उपयोग कैसे करते हैं:

    extern Queue downstream, upstream;

    int Example()
    {
        Finally atEnd([](){ 
           downstream.push(nullptr);
        });
        while(!ExitRequested())
        {
           X* x = upstream.pop();
           if (!x) break;
           x->doSomething();
           downstream.push(x);
        }
    }

नमस्ते, मेरा मानना ​​है कि ऊपर मेरा जवाब ( stackoverflow.com/a/38701485/566849 ) आपकी आवश्यकताओं को पूरी तरह से संतुष्ट करता है।
फैबियो ए।

1

जैसा कि कई लोगों ने कहा है, समाधान अंत में ब्लॉक से बचने के लिए C ++ 11 सुविधाओं का उपयोग करना है। सुविधाओं में से एक है unique_ptr

यहाँ Mephane का उत्तर RAII पैटर्न का उपयोग करके लिखा गया है।

#include <vector>
#include <memory>
#include <list>
using namespace std;

class Foo
{
 ...
};

void DoStuff(vector<string> input)
{
    list<unique_ptr<Foo> > myList;

    for (int i = 0; i < input.size(); ++i)
    {
      myList.push_back(unique_ptr<Foo>(new Foo(input[i])));
    }

    DoSomeStuff(myList);
}

C ++ मानक लाइब्रेरी कंटेनरों के साथ unique_ptr का उपयोग करने के लिए कुछ और परिचय यहां दिए गए हैं


0

मैं एक विकल्प प्रदान करना चाहूंगा।

यदि आप चाहते हैं कि अंततः ब्लॉक को हमेशा कहा जाता है, तो इसे अंतिम कैच ब्लॉक के बाद डालें (जो कि संभवत: catch( ... )अपवाद नहीं पकड़ा जाना चाहिए )

try{
   // something that might throw exception
} catch( ... ){
   // what to do with uknown exception
}

//final code to be called always,
//don't forget that it might throw some exception too
doSomeCleanUp(); 

यदि आप अंत में एक अंतिम चीज के रूप में ब्लॉक करना चाहते हैं, जब कोई अपवाद फेंका जाता है, तो आप बूलियन स्थानीय चर का उपयोग कर सकते हैं - चलाने से पहले आप इसे गलत पर सेट करें और कोशिश ब्लॉक के बहुत अंत में सच्चा असाइनमेंट डालें, फिर चर के लिए ब्लॉक चेक को पकड़ने के बाद मूल्य:

bool generalAppState = false;
try{
   // something that might throw exception

   //the very end of try block:
   generalAppState = true;
} catch( ... ){
   // what to do with uknown exception
}

//final code to be called only when exception was thrown,
//don't forget that it might throw some exception too
if( !generalAppState ){
   doSomeCleanUpOfDirtyEnd();
}

//final code to be called only when no exception is thrown
//don't forget that it might throw some exception too
else{
   cleanEnd();
}

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

1
@ स्पष्ट रूप से आपकी राय पवित्र नहीं है, मुझे यह बात समझ में आती है, लेकिन C ++ में ऐसी कोई बात नहीं है, इसलिए आपको इसे एक शीर्ष परत के रूप में समझना होगा जो इस व्यवहार का अनुकरण करती है।
jave.web

डाउनलोड = कृपया टिप्पणी :)
jave.web

0

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

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

हाँ यह उन वर्गों के लिए ठीक है जो किसी विशेष संसाधन, या सामान्य लोगों को प्रबंधित करने के लिए डिज़ाइन किए गए हैं जो समान संसाधनों के एक सेट को संभालने के लिए डिज़ाइन किए गए हैं। लेकिन, भले ही शामिल सभी चीजों में इस तरह के रैपर हों, हो सकता है कि सफाई का समन्वय न केवल विनाशकारियों के रिवर्स ऑर्डर आह्वान में एक सरल हो।

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


-2
try
{
  ...
  goto finally;
}
catch(...)
{
  ...
  goto finally;
}
finally:
{
  ...
}

35
प्यारा मुहावरा, लेकिन यह काफी समान नहीं है। कोशिश ब्लॉक या कैच में लौटने से आपके 'अंत:' कोड नहीं होंगे।
एडवर्ड केमेट

10
यह गलत उत्तर (0 रेटिंग के साथ) रखने के लायक है, क्योंकि एडवर्ड केमट एक बहुत महत्वपूर्ण अंतर लाता है।
निशान लता

12
इससे भी बड़ा दोष (IMO): यह कोड सभी अपवादों को खाता है, जो finallyऐसा नहीं करता है।
बेन वोइग्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.