c++ - मुझे "कोशिश"-"पकड़" में हर ब्लॉक को क्यों लपेटना चाहिए?




exception exception-handling (10)

मैं हमेशा इस धारणा का रहा हूं कि यदि कोई विधि अपवाद फेंक सकती है तो यह बेकार है कि इस कॉल को सार्थक प्रयास ब्लॉक से सुरक्षित न करें।

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


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

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

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

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

int ret = SaveFirstSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

ret = SaveSecondSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

ret = SaveThirdSection();

if (ret == FAILED)
{
    /* some diagnostic */
    return;
}

यहां बताया गया है कि अपवादों के साथ यह कैसे लिखा जा सकता है:

// these throw if failed, caught in SaveDocument's catch
SaveFirstSection();
SaveSecondSection();
SaveThirdSection();

अब यह हो रहा है कि क्या हो रहा है।

नोट अपवाद सुरक्षित कोड अन्य तरीकों से लिखने के लिए trickier हो सकता है: यदि कोई अपवाद फेंक दिया जाता है तो आप किसी भी स्मृति को रिसाव नहीं करना चाहते हैं। सुनिश्चित करें कि आप RAII , एसटीएल कंटेनर, स्मार्ट पॉइंटर्स और अन्य ऑब्जेक्ट्स के बारे में जानते हैं जो विनाशकों में अपने संसाधनों को मुक्त करते हैं, क्योंकि ऑब्जेक्ट्स हमेशा अपवादों से पहले नष्ट हो जाते हैं।


आपको अपने कोड के हर हिस्से को try-catch के अंदर कवर करने की आवश्यकता नहीं है। try-catch ब्लॉक का मुख्य उपयोग हैडलिंग को हल करना और आपके प्रोग्राम में बग / अपवाद प्राप्त करना है। try-catch कुछ उपयोग -

  1. आप इस ब्लॉक का उपयोग कर सकते हैं जहां आप अपवाद को संभालना चाहते हैं या बस आप कह सकते हैं कि लिखित कोड का ब्लॉक अपवाद फेंक सकता है।
  2. यदि आप अपने उपयोग के तुरंत बाद अपनी वस्तुओं का निपटान करना चाहते हैं, तो आप try-catch ब्लॉक का उपयोग कर सकते हैं।

एक विधि को केवल एक अपवाद पकड़ना चाहिए जब यह इसे कुछ समझदार तरीके से संभाल सके।

अन्यथा, उम्मीद है कि कॉल स्टैक को ऊपर उठाने वाला एक तरीका इसे समझ सकता है।

जैसा कि अन्य ने ध्यान दिया है, कॉल स्टैक के उच्चतम स्तर पर एक अनचाहे अपवाद हैंडलर (लॉगिंग के साथ) रखना अच्छा अभ्यास है ताकि यह सुनिश्चित किया जा सके कि कोई घातक त्रुटियां लॉग हैं।


क्योंकि अगला सवाल यह है कि "मैंने अपवाद पकड़ा है, मैं आगे क्या करूँ?" आप क्या करेंगे? यदि आप कुछ भी नहीं करते हैं - यह छिपाने में त्रुटि है और प्रोग्राम क्या हुआ यह जानने के किसी भी मौका के बिना "बस काम नहीं कर सकता"। आपको यह समझने की जरूरत है कि अपवाद पकड़े जाने के बाद आप वास्तव में क्या करेंगे और केवल अगर आप जानते हैं तो पकड़ लें।


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

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


मेरे कंप्यूटर विज्ञान प्रोफेसर ने मुझे एक बार सलाह दी थी: "मानक साधनों का उपयोग करके त्रुटि को संभालना संभव नहीं है, केवल तभी प्रयास करें और ब्लॉक को पकड़ें।"

एक उदाहरण के रूप में, उन्होंने हमें बताया कि यदि कोई कार्यक्रम किसी ऐसे स्थान पर कुछ गंभीर समस्या में भाग गया जहां ऐसा कुछ करना संभव नहीं है:

int f()
{
    // Do stuff

    if (condition == false)
        return -1;
    return 0;
}

int condition = f();

if (f != 0)
{
    // handle error
}

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


मैं सबसे कम स्तर पर जितना संभव हो उतने अपवादों को संभालने के लिए आपके प्रश्न की मूल दिशा से सहमत हूं।

कुछ मौजूदा उत्तर जैसे "आपको अपवाद को संभालने की आवश्यकता नहीं है। कोई और इसे ढेर कर देगा।" मेरे अनुभव के लिए वर्तमान में विकसित कोड के अपवाद हैंडलिंग के बारे में नहीं सोचने का एक बुरा बहाना है , अपवाद को किसी और की समस्या को संभालने में अपवाद करना।

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


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

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

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

इस तकनीक का इस्तेमाल 12 देवताओं द्वारा 2 साल से विकसित फॉर्च्यून 500 कंपनी में एक बग्गी ऐप को तुरंत स्थिर करने के लिए किया गया था। इसका उपयोग करके मैंने पहचान की, तय की, परीक्षण का परीक्षण किया, और 4 महीनों में 3000 फिक्स्ड तैनात किए, जिस स्थिति में सिस्टम ने अब तक किसी भी अपवाद की सूचना नहीं दी थी। यह औसतन 15 मिनट के लिए हर 15 मिनट में एक फिक्स के लिए औसत है।


यदि आप हमेशा एक विधि के कॉलर में अपवादों को संभालते हैं जो अपवाद फेंक सकता है, तो अपवाद बेकार हो जाते हैं, और आप बेहतर त्रुटि कोड का उपयोग करेंगे।

अपवादों का पूरा बिंदु यह है कि उन्हें कॉल श्रृंखला में हर विधि में संभालने की आवश्यकता नहीं है।


सबसे अच्छी सलाह मैंने सुना है कि आपको केवल उन बिंदुओं पर अपवादों को पकड़ना चाहिए जहां आप असाधारण स्थिति के बारे में कुछ समझदारी से कर सकते हैं, और "पकड़, लॉग और रिलीज" एक अच्छी रणनीति नहीं है (यदि कभी-कभी पुस्तकालयों में अपरिहार्य है)।





try-catch