क्या `कैच (…) {फेंकना है; } `एक बुरा अभ्यास?


74

हालांकि मैं मानता हूं कि ... पुनर्विचार के बिना पकड़ना वास्तव में गलत है, लेकिन मेरा मानना ​​है कि इस तरह के निर्माणों का उपयोग करना:

try
{
  // Stuff
}
catch (...)
{
  // Some cleanup
  throw;
}

उन मामलों में स्वीकार्य है जहां RAII लागू नहीं है । (कृपया, यह मत पूछो ... मेरी कंपनी में हर कोई ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग पसंद नहीं करता है और RAII को अक्सर "बेकार स्कूल सामान" के रूप में देखा जाता है ...)

मेरे सहकर्मियों का कहना है कि आपको हमेशा पता होना चाहिए कि किन अपवादों को फेंकना है और आप हमेशा जैसे निर्माण का उपयोग कर सकते हैं:

try
{
  // Stuff
}
catch (exception_type1&)
{
  // Some cleanup
  throw;
}
catch (exception_type2&)
{
  // Some cleanup
  throw;
}
catch (exception_type3&)
{
  // Some cleanup
  throw;
}

क्या इन स्थितियों के बारे में अच्छी तरह से प्रशंसा की गई है?


3
@Pubby: यकीन नहीं कि यह वही सवाल है। जुड़ा हुआ प्रश्न "मुझे पकड़ना चाहिए" के बारे में अधिक है, ...जबकि मेरा प्रश्न "क्या मुझे बेहतर पकड़ना चाहिए ...या फिर <specific exception>से पहले फेंकना चाहिए"
ेरेऑन

53
यह कहने के लिए क्षमा करें, लेकिन RAII के बिना C ++ C ++ नहीं है।
fredoverflow

46
तो आपके गौ-कर्मचारियों ने उस तकनीक को खारिज कर दिया, जिसे एक निश्चित समस्या से निपटने के लिए आविष्कार किया गया था और फिर इस बारे में विचार करने के लिए कि निम्न में से किस विकल्प का उपयोग किया जाना चाहिए? कहने के लिए क्षमा करें, लेकिन यह बेवकूफ लगता है , कोई फर्क नहीं पड़ता कि जिस तरह से मैं इसे देखता हूं।
sbi

11
"पकड़ना ... पुनर्विचार के बिना वास्तव में गलत है" - आप गलत हैं। में main, catch(...) { return EXIT_FAILURE; }अच्छी तरह से है कि एक डिबगर के तहत नहीं चल रहा है कोड में सही हो सकता है। यदि आप नहीं पकड़ते हैं, तो स्टैक अनचाहे नहीं हो सकता है। यह केवल तभी होता है जब आपका डिबगर बिना किसी अपवाद के पता लगाता है कि आप उन्हें छोड़ना चाहते हैं main
स्टीव जेसोप

3
... तो यह एक "प्रोग्रामिंग त्रुटि" है, भले ही यह जरूरी नहीं है कि आप इसके बारे में जानना नहीं चाहते हैं। वैसे भी, आपके सहकर्मी अच्छे सॉफ्टवेयर पेशेवर नहीं हैं, इसलिए जैसा कि sbi का कहना है कि इस बारे में बात करना बहुत मुश्किल है कि ऐसी स्थिति से कैसे निपटा जाए जो शुरू होने के लिए कालानुक्रमिक रूप से कमजोर हो।
स्टीव जेसोप

जवाबों:


196

मेरे सहकर्मियों का कहना है कि आपको हमेशा पता होना चाहिए कि किन अपवादों को फेंकना है [...]

आपका सहकर्मी, मुझे यह कहने से नफरत होगी, जाहिर तौर पर सामान्य-उद्देश्य पुस्तकालयों पर कभी काम नहीं किया गया है।

दुनिया में कैसे एक वर्ग std::vectorभी यह जानने का दिखावा कर सकता है कि कॉपी निर्माणकर्ता क्या फेंकेंगे, जबकि अभी भी अपवाद सुरक्षा की गारंटी दे रहे हैं?

यदि आप हमेशा जानते थे कि संकलन-समय पर कैली क्या करेगी, तो बहुरूपता बेकार हो जाएगी! कभी-कभी पूरे लक्ष्य को दूर करना होता है जो निचले स्तर पर होता है, इसलिए आप विशेष रूप से जानना नहीं चाहते कि क्या हो रहा है!


32
दरअसल, भले ही उन्हें पता हो कि अपवादों को फेंकना है। इस कोड के दोहराव का उद्देश्य क्या है? जब तक हैंडलिंग अलग नहीं होती है, मुझे आपके ज्ञान को दिखाने के लिए अपवादों की गणना करने में कोई मतलब नहीं है।
माइकल क्रेलिन - हैकर

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

4
और जो मुझे परेशान करता है वह यह है कि "बेकार स्कूल सामग्री" के रूप में उपयोगी और सुविधाजनक तकनीक को देखने के साथ युग्मित होने पर इस तरह के दृष्टिकोण की उत्पत्ति क्या हो सकती है। लेकिन अच्छी तरह से ...
माइकल क्रेलिन - हैकर

1
+1, सभी संभावित विकल्पों की गणना भविष्य की विफलता के लिए एक उत्कृष्ट नुस्खा है, तो पृथ्वी पर कोई ऐसा क्यों करेगा ...?
littleadv

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

44

क्या आप में पकड़ा जा रहा है लगता है कि किसी को अपने केक है और यह भी खाने की कोशिश कर के विशिष्ट नरक है।

RAII और अपवादों को हाथ से जाने के लिए डिज़ाइन किया गया है। RAII वह साधन है जिसके द्वारा आपको सफाई करने के लिए बहुत सारे कथन लिखने की आवश्यकता नहीं हैcatch(...) । यह स्वचालित रूप से होगा, निश्चित रूप से। और अपवाद आरएआईआई वस्तुओं के साथ काम करने का एकमात्र तरीका है, क्योंकि निर्माणकर्ता केवल सफल या वस्तु को एक त्रुटि स्थिति में डाल सकते हैं या फेंक सकते हैं, लेकिन कौन चाहता है?)।

एक catchबयान दो चीजों में से एक कर सकता है: एक त्रुटि या असाधारण परिस्थिति को संभालना, या सफाई का काम करना। कभी-कभी यह दोनों करता है, लेकिन catchइनमें से कम से कम एक करने के लिए प्रत्येक कथन मौजूद है।

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

आरएआई आपको अपवाद हैंडलिंग के संबंध में क्या देता है, निशुल्क सफाई है। यदि सब कुछ आरएआई ठीक से समझाया जाता है, तो सब कुछ ठीक से साफ हो जाएगा। अब आपको catchकथन सफाई करने की आवश्यकता नहीं है । किस मामले में, एक catch(...)बयान लिखने का कोई कारण नहीं है ।

तो मैं सहमत हूँ कि catch(...)ज्यादातर बुराई है ... अनंतिम रूप से

यह प्रावधान RAII का उचित उपयोग है। क्योंकि इसके बिना, आपको कुछ सफाई करने में सक्षम होने की आवश्यकता है। इसके आसपास कोई नहीं मिल रहा है; आपको सफाई कार्य करने में सक्षम होना चाहिए। आपको यह सुनिश्चित करने में सक्षम होना चाहिए कि एक अपवाद को फेंकने से कोड एक उचित स्थिति में निकल जाएगा। और catch(...)ऐसा करने में एक महत्वपूर्ण उपकरण है।

आप एक के बिना दूसरा नहीं रख सकते है। आप यह नहीं कह सकते हैं कि दोनों RAII और catch(...) खराब हैं। आपको इनमें से कम से कम एक की आवश्यकता है; अन्यथा, आप सुरक्षित नहीं हैं।

बेशक, वहाँ एक वैध-हालांकि-दुर्लभ उपयोग है catch(...)कि RAII भी नहीं मिट सकता है: exception_ptrकिसी और को आगे करने के लिए। आमतौर पर एक promise/futureया समान इंटरफ़ेस के माध्यम से ।

मेरे सहकर्मियों का कहना है कि आपको हमेशा पता होना चाहिए कि किन अपवादों को फेंकना है और आप हमेशा जैसे निर्माण का उपयोग कर सकते हैं:

आपका सहकर्मी एक मूर्ख (या सिर्फ बहुत अज्ञानी) है। यह तुरंत स्पष्ट होना चाहिए कि वह कितना कॉपी-एंड-पेस्ट कोड बता रहा है, जो आप लिखते हैं। उन कैच स्टेटमेंट्स में से प्रत्येक के लिए क्लीनअप बिल्कुल समान होगा । यह एक दुःस्वप्न है, पठनीयता का उल्लेख नहीं है।

संक्षेप में: यह वह समस्या है जिसे आरएआईआई ने हल करने के लिए बनाया था (यह नहीं कि यह अन्य समस्याओं को हल नहीं करता है)।

इस धारणा के बारे में मुझे क्या भ्रम है कि यह आमतौर पर पीछे की ओर है कि ज्यादातर लोग यह तर्क देते हैं कि RAII खराब है। आम तौर पर, तर्क यह है कि "RAII खराब है क्योंकि आपको निर्माण विफलता का संकेत करने के लिए अपवादों का उपयोग करना है। लेकिन आप अपवादों को फेंक नहीं सकते हैं, क्योंकि यह सुरक्षित नहीं है और आपको catchसब कुछ साफ करने के लिए बहुत सारे बयान देने होंगे।" जो एक टूटा हुआ तर्क है क्योंकि RAII उस समस्या को हल करता है जो RAII की कमी पैदा करती है।

संभावना से अधिक, वह RAII के खिलाफ है क्योंकि यह विवरण छुपाता है। विध्वंसक कॉल स्वचालित चर पर तुरंत दिखाई नहीं देते हैं। तो आपको कोड मिलता है जिसे अव्यक्त रूप से कहा जाता है। कुछ प्रोग्रामर वास्तव में नफरत करते हैं। जाहिरा तौर पर, उस बिंदु पर जहां उन्हें लगता है कि 3 catchकथन हैं, जो सभी कॉपी-एंड-पेस्ट कोड के साथ एक ही काम करते हैं, एक बेहतर विचार है।


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

@anton_rh: शायद, लेकिन यहां तक ​​कि उन मामलों में, कैच-ऑल स्टेटमेंट अंतिम उपाय का उपकरण है । पसंदीदा उपकरण वह सब कुछ करना है जो आपके द्वारा किसी भी स्थिति को बदलने से पहले फेंकता है जिसे आपको अपवाद पर वापस करना होगा। जाहिर है कि आप सभी मामलों में उस तरह से सब कुछ लागू नहीं कर सकते हैं, लेकिन मजबूत अपवाद गारंटी प्राप्त करने का आदर्श तरीका है।
निकोल बोल्स

14

दो टिप्पणियाँ, वास्तव में। पहला यह है कि एक आदर्श दुनिया में रहते हुए, आपको हमेशा पता होना चाहिए कि क्या अपवाद हैं, व्यवहार में, यदि आप तीसरे पक्ष के पुस्तकालयों के साथ काम कर रहे हैं, या Microsoft संकलक के साथ संकलन कर रहे हैं, तो आप नहीं करते हैं। हालांकि, इस बिंदु पर अधिक; यहां तक ​​कि अगर आप सभी संभावित अपवादों को ठीक से जानते हैं, तो क्या यह प्रासंगिक है? catch (...)इरादे को कहीं बेहतर तरीके से व्यक्त करता है catch ( std::exception const& ), यहां तक ​​कि यह भी कि सभी संभावित अपवादों से व्युत्पन्न है std::exception(जो एक आदर्श दुनिया में मामला होगा)। कई कैच ब्लॉक का उपयोग करने के लिए, यदि सभी अपवादों के लिए कोई सामान्य आधार नहीं है: जो कि सर्वथा औचित्य है, और एक रखरखाव दुःस्वप्न है। आप कैसे पहचानते हैं कि सभी व्यवहार समान हैं? और यही इरादा था? और यदि आपको व्यवहार को बदलना है तो क्या होगा (उदाहरण के लिए बग फिक्स)? यह सब बहुत आसान है एक याद आती है।


3
वास्तव में, मेरे सहकर्मी अपने ही अपवाद वर्ग जो करता है डिज़ाइन किया गया नहीं से निकाले जाते हैं std::exceptionऔर हमारे codebase के बीच इसके उपयोग को लागू करने की रोजमर्रा की कोशिश करता है। मेरा अनुमान है कि उन्होंने कोड और बाहरी पुस्तकालयों का उपयोग करने के लिए मुझे दंडित करने की कोशिश की, जो उन्होंने खुद नहीं लिखे हैं।
ेरेऑन

17
@ मेरे लिए ऐसा लगता है कि आपके सहकर्मी को प्रशिक्षण की सख्त आवश्यकता है। किसी भी मामले में, मैं शायद उसके द्वारा लिखे गए पुस्तकालयों का उपयोग करने से बचूंगा।

2
टेम्प्लेट और यह जानना कि अपवादों को फेंक दिया जाएगा जैसे मूंगफली का मक्खन और मृत जेकॉस। कुछ के रूप में सरल कुछ std::vector<>भी किसी भी कारण के लिए अपवाद के किसी भी प्रकार फेंक सकते हैं।
डेविड थॉर्नले 15

3
कृपया हमें बताएं, आपको कैसे पता चलेगा कि कल के बग को किस अपवाद के रूप में फेंक दिया जाएगा?
मटनज़ सिप

11

मुझे लगता है कि आपके सहकर्मी ने कुछ अच्छी सलाह दी है - आपको एक catchब्लॉक में केवल ज्ञात अपवादों को संभालना चाहिए जब आप उन्हें फिर से नहीं फेंकेंगे।

इसका मतलब है की:

try
{
  // Stuff
}
catch (...)
{
  // General stuff
}

बुरा है क्योंकि यह चुपचाप किसी भी त्रुटि को छिपाएगा ।

हालाँकि:

try
{
  // Stuff
}
catch (exception_type_we_can_handle&)
{
  // Deal with the known exception
}

ठीक है - हम जानते हैं कि हम क्या कर रहे हैं और इसे कॉलिंग कोड में उजागर करने की आवश्यकता नहीं है।

इसी तरह:

try
{
  // Stuff
}
catch (...)
{
  // Rollback transactions, log errors, etc
  throw;
}

ठीक है, सबसे अच्छा अभ्यास है, यहां तक ​​कि सामान्य त्रुटियों से निपटने के लिए कोड उस कोड के साथ होना चाहिए जो उनके कारण होता है। यह कैलली पर भरोसा करने से बेहतर है कि यह जानना चाहिए कि लेन-देन को वापस करने की जरूरत है या जो भी हो।


9

हां या ना का कोई भी जवाब इस तर्क के साथ होना चाहिए कि ऐसा क्यों है।

यह कहना कि यह सिर्फ इसलिए गलत है क्योंकि मुझे सिखाया गया है कि यह तरीका केवल अंध कट्टरता है।

//Some cleanup; throwकई बार वही लिखना , जैसा कि आपके उदाहरण में गलत है क्योंकि कोड दोहराव है और यह एक रखरखाव का बोझ है। इसे सिर्फ एक बार लिखना बेहतर है।

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

लेकिन अगर आप एक catch(...)के बाद फिर से डूब जाते हैं , तो बाद का तर्क अब लागू नहीं होता है, क्योंकि आप वास्तव में अपवाद को नहीं संभाल रहे हैं, इसलिए कोई कारण नहीं है कि इसे हतोत्साहित किया जाए।

वास्तव में मुझे बिना किसी समस्या के संवेदनशील कार्यों में प्रवेश करने के लिए किया गया है:

void DoSomethingImportant()
{
    try
    {
        Log("Going to do something important");
        DoIt();
    }
    catch (std::exception &e)
    {
        Log("Error doing something important: %s", e.what());
        throw;
    }
    catch (...)
    {
        Log("Unexpected error doing something important");
        throw;
    }
    Log("Success doing something important");
}

2
चलो आशा है कि Log(...)फेंक नहीं सकते।
डेडुप्लिकेटर

2

मैं आम तौर पर यहां के पदों के मूड से सहमत हूं, मैं वास्तव में विशिष्ट अपवादों को पकड़ने के पैटर्न को नापसंद करता हूं - मुझे लगता है कि इसका सिंटैक्स अभी भी शैशवावस्था में है और अभी तक अनावश्यक कोड का सामना करने में सक्षम नहीं है।

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

मैं "ऑलवेज यूज़ एक्स" के रवैये को सही नहीं ठहरा रहा हूँ, बस यह कह रहा हूँ कि कभी-कभी यह वास्तव में उन्हें सूचीबद्ध देखना अच्छा लगता है और मुझे यकीन है कि कुछ लोगों को लगता है कि यह "राइट" रास्ता है।

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