अपवाद के रूप में या त्रुटियों के रूप में?


10

मैं एक पेशेवर सी प्रोग्रामर और एक शौक़ीन ओब्ज-सी प्रोग्रामर (ओएस एक्स) हूं। हाल ही में मुझे C ++ में विस्तार करने के लिए लुभाया गया है, क्योंकि यह बहुत समृद्ध वाक्यविन्यास है।

अब तक कोडिंग मैंने अपवादों से ज्यादा नहीं की है। उद्देश्य-सी उनके पास है, लेकिन एप्पल की नीति काफी सख्त है:

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

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

यहाँ प्रोग्रामर पर मैंने कई उत्तर पाए हैं या तो त्रुटि कोड के बजाय अपवाद का उपयोग करने की घोषणा की है या अपवाद का उपयोग नहीं किया है । पूर्व अधिक प्रचलित लगता है।

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

संपादित करें: हालांकि पूरी तरह से प्रासंगिक नहीं है, मुझे शायद स्पष्ट करना चाहिए कि क्या nilहै। तकनीकी रूप से यह वैसा ही है NULL, लेकिन बात यह है कि, एक संदेश भेजना ठीक है nil। तो आप कुछ ऐसा कर सकते हैं

NSError *err = nil;
id obj = [NSFileHandle fileHandleForReadingFromURL:myurl error:&err];

[obj retain];

भले ही पहला कॉल वापस आए nil। और जैसा कि आप *objओब्ज-सी में कभी भी नहीं करते हैं , वहाँ एक नाल पॉइंटर डेरेफेरेंस का कोई जोखिम नहीं है।


Imho, यह बेहतर होगा यदि आप उद्देश्य सी कोड का एक टुकड़ा दिखाएंगे कि आप वहां त्रुटियों के साथ कैसे काम करते हैं। ऐसा लगता है, लोग आपसे जो पूछ रहे हैं उससे कुछ अलग बात कर रहे हैं।
गंगानुस ०us

एक तरह से मुझे लगता है कि मैं अपवादों का उपयोग करने के औचित्य की तलाश कर रहा हूं। चूंकि मुझे कुछ अंदाजा है कि वे कैसे लागू होते हैं, मुझे पता है कि वे कितने महंगे हो सकते हैं। लेकिन मुझे लगता है कि अगर मुझे उनके उपयोग के लिए एक अच्छा पर्याप्त तर्क मिल सकता है तो मैं उस रास्ते पर जाऊंगा।
प्रति जोहानसन

ऐसा लगता है, सी ++ के लिए आपको उनका उपयोग न करने के लिए औचित्य की आवश्यकता है। यहां प्रतिक्रियाओं के अनुसार।
गंगनुस

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

"मुझे पसंद नहीं है ... असाधारण नहीं हैं चीजों के लिए अपवाद का उपयोग करना" - सहमत हुए।
गंगनुस

जवाबों:


1

C ++ अपवादों का अधिक बार उपयोग करना पसंद करता है।

मैं वास्तव में कुछ मामलों में ऑब्जेक्टिव-सी से कम का सुझाव दूंगा क्योंकि सी ++ मानक लाइब्रेरी आमतौर पर प्रोग्रामर त्रुटियों पर नहीं फेंकती है, जैसे कि इसके सबसे सामान्य मामले डिजाइन फॉर्म (में operator[], यानी) में रैंडम-एक्सेस सीक्वेंस के आउट-ऑफ-बाउंड एक्सेस की तरह। अमान्य पुनरावृत्ति को रोकने के लिए प्रयास कर रहा है। भाषा किसी सरणी को सीमा से बाहर जाने, या अशक्त सूचक, या इस प्रकार की किसी भी चीज़ को एक्सेस करने पर नहीं फेंकती है।

प्रोग्रामर गलतियों को बड़े पैमाने पर अपवाद-हैंडलिंग समीकरण से बाहर ले जाना वास्तव में त्रुटियों की एक बहुत बड़ी श्रेणी को दूर करता है जो अन्य भाषाएं अक्सर जवाब देती हैं throwing। C ++ को जाता है assert(जो रिलीज / प्रोडक्शन बिल्ड, केवल डिबग बिल्ड) में संकलित नहीं होता है या ऐसे मामलों में सिर्फ गड़बड़ (अक्सर दुर्घटनाग्रस्त) हो जाता है, शायद इसलिए क्योंकि भाषा ऐसे रनटाइम चेक की लागत को लागू नहीं करना चाहती है जब तक कि प्रोग्रामर विशेष रूप से कोड लिखकर लागतों का भुगतान नहीं करना चाहता है जब तक कि ऐसे चेक स्वयं / स्वयं नहीं करता है, तब तक ऐसी प्रोग्रामर गलतियों का पता लगाना आवश्यक है।

सी + + कोडिंग मानकों में ऐसे मामलों में अपवादों से बचने के लिए सटर भी प्रोत्साहित करता है:

एक प्रोग्रामिंग त्रुटि की रिपोर्ट करने के लिए एक अपवाद का उपयोग करने का प्राथमिक नुकसान यह है कि आप वास्तव में स्टैक अनइंडिंग नहीं चाहते हैं जब आप डिबगर को उस सटीक लाइन पर लॉन्च करना चाहते हैं जहां उल्लंघन का पता चला था, तो लाइन की स्थिति बरकरार है। संक्षेप में: ऐसी त्रुटियां हैं जिन्हें आप जानते हैं कि ऐसा हो सकता है (आइटम 69 से 75 देखें)। बाकी सब के लिए जो नहीं होना चाहिए, और यह प्रोग्रामर की गलती है अगर यह करता है, तो है assert

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

बाहरी अपवाद

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

लेन-देन

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

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

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

प्रदर्शन

अपवादों का उपयोग करने या न करने के लिए एक अन्य मार्गदर्शक कारक प्रदर्शन है, और मुझे कुछ जुनूनी, पैसा-चुटकी, काउंटर-उत्पादक तरीके से मतलब नहीं है। बहुत सारे C ++ कंपाइलर लागू होते हैं जिन्हें "जीरो-कॉस्ट एक्सेप्शन हैंडलिंग" कहा जाता है।

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

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

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

गैर त्रुटियाँ

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

उदाहरण के लिए, एक सेट प्रतिच्छेदन फ़ंक्शन दो सेटों के माध्यम से लूप करना चाहता है और उन कुंजियों की खोज कर सकता है जो उनके पास हैं। यदि एक कुंजी खोजने में विफल रहा है threw, तो आप के माध्यम से पाशन होगा और आधे या अधिक पुनरावृत्तियों में अपवादों का सामना कर सकता है:

Set<int> set_intersection(const Set<int>& a, const Set<int>& b)
{
     Set<int> intersection;
     for (int key: a)
     {
          try
          {
              b.find(key);
              intersection.insert(other_key);
          }
          catch (const KeyNotFoundException&)
          {
              // Do nothing.
          }
     }
     return intersection;
}

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

उन प्रकार के मामलों के लिए, कुछ प्रकार के रिटर्न वैल्यू विफलता का संकेत देते हैं (कुछ भी falseएक अमान्य पुनरावृत्ति करने वाले से nullptrया जो भी संदर्भ में समझ में आता है) आमतौर पर कहीं अधिक उपयुक्त होता है, और अक्सर गैर-त्रुटि प्रकार के बाद भी अधिक व्यावहारिक और उत्पादक होता है मामला आमतौर पर एनालॉग catchसाइट तक पहुंचने के लिए कुछ स्टैक अनइंडिंग प्रक्रिया के लिए कॉल नहीं करता है ।

प्रशन

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

C ++ में एकमुश्त अपवाद से बचना मेरे लिए बेहद प्रति-उत्पादक है, जब तक कि आप कुछ एम्बेडेड सिस्टम या किसी विशेष प्रकार के मामले में काम नहीं कर रहे हैं जो उनके उपयोग को मना करता है (जिस स्थिति में आपको सभी से बचने के लिए अपने रास्ते से बाहर जाना होगा। पुस्तकालय और भाषा की कार्यक्षमता जो अन्यथा throwसख्ती से उपयोग की तरह होगी nothrow new)।

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

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

if ((err = ApiCall(...)) != success)
{
     // Handle error
}

... कोड की लगभग हर एक पंक्ति में एपीआई को चेक करने के लिए इस तरह के चेक की आवश्यकता होती है। फिर भी मुझे टीमों के साथ काम करने का सौभाग्य नहीं मिला है। वे अक्सर ऐसी त्रुटियों को अनदेखा करते हैं आधा, कभी-कभी सबसे अधिक, समय का भी। यह मेरे लिए सबसे बड़ी अपील है। यदि हम इस एपीआई को लपेटते हैं और इसे throwत्रुटि का सामना करने पर समान रूप से बनाते हैं , तो अपवाद को संभवतः नजरअंदाज नहीं किया जा सकता है , और मेरे विचार में, और अनुभव, यही वह जगह है जहां अपवादों की श्रेष्ठता निहित है।

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

यह मुझे लगता है कि चूंकि संकेत दुर्लभ हैं, मुझे अपवादों से बचने के लिए चुनने पर आंतरिक त्रुटि झंडे के साथ जाना होगा।

मैं जरूरी नहीं कहूंगा कि संकेत दुर्लभ हैं। C ++ 11 और उसके बाद के कंटेनर के अंतर्निहित डेटा पॉइंटर्स और एक नए nullptrकीवर्ड को प्राप्त करने के लिए अभी भी विधियाँ हैं । आम तौर पर कच्चे संकेत का उपयोग करने के लिए नासमझ माना जाता है / स्मृति का प्रबंधन यदि आप unique_ptrइसके बजाय कुछ का उपयोग कर सकते हैं तो यह महत्वपूर्ण है कि अपवाद की उपस्थिति में RAII-अनुरूप होना कितना महत्वपूर्ण है। लेकिन कच्चे पॉइंटर्स जो स्मृति का प्रबंधन / प्रबंधन नहीं करते हैं, उन्हें बहुत बुरा नहीं माना जाता है (यहां तक ​​कि सटर और स्ट्रॉस्ट्रुप जैसे लोगों से भी) और कभी-कभी चीजों को इंगित करने के तरीके के रूप में बहुत व्यावहारिक (सूचकांकों के साथ जो चीजों को इंगित करते हैं)।

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


14

यहाँ यह बात है: C ++ के अद्वितीय इतिहास और लचीलेपन के कारण, आप किसी को किसी भी विशेषता के बारे में कोई राय देने की घोषणा कर सकते हैं जो आप चाहते हैं। हालाँकि, सामान्य तौर पर, जो आप कर रहे हैं वह सी की तरह दिखता है, यह एक बुरा विचार है।

अपवादों की बात आती है तो C ++ बहुत कम होने के कारणों में से एक यह है कि return nilजब भी आप ऐसा महसूस करते हैं तो आप बिल्कुल नहीं कर सकते । nilअधिकांश मामलों और प्रकारों में ऐसी कोई बात नहीं है ।

लेकिन यहाँ सरल तथ्य है। अपवाद अपना काम अपने आप करते हैं। जब आप एक अपवाद फेंकते हैं, तो RAII पर कब्जा हो जाता है और सब कुछ संकलक द्वारा नियंत्रित किया जाता है। जब आप त्रुटि कोड का उपयोग करते हैं, तो आपको उन्हें जांचना याद रखना होगा। यह स्वाभाविक रूप से अपवादों को त्रुटि कोड की तुलना में अधिक सुरक्षित बनाता है- वे स्वयं की जांच करते हैं, जैसा कि यह था। इसके अलावा, वे अधिक अभिव्यंजक हैं। एक अपवाद फेंकें और आप एक स्ट्रिंग बता सकते हैं जो आपको बताती है कि त्रुटि क्या है, और यहां तक ​​कि विशिष्ट जानकारी भी हो सकती है, जैसे "खराब पैरामीटर एक्स जो जेड के बजाय मूल्य वाई था"। 0xDEADBEEF का एक त्रुटि कोड प्राप्त करें और क्या, वास्तव में, गलत हो गया है? मुझे यकीन है कि आशा है कि प्रलेखन पूर्ण और अप-टू-डेट है, और यहां तक कि यदि आप "खराब पैरामीटर" मिलता है, यह कभी नहीं बताने के लिए जा रहा है जोपैरामीटर, इसका क्या मूल्य था, और इसका क्या मूल्य होना चाहिए था। यदि आप संदर्भ द्वारा पकड़ते हैं, साथ ही, वे बहुरूपी हो सकते हैं। अंत में, अपवाद उन स्थानों से फेंके जा सकते हैं जहां त्रुटि कोड कभी भी नहीं हो सकते हैं, जैसे निर्माणकर्ता। और जेनेरिक एल्गोरिदम के बारे में कैसे? कैसे std::for_eachअपने त्रुटि कोड को संभालने के लिए जा रहा है? प्रो-टिप: यह नहीं है।

अपवाद हर लिहाज से त्रुटि कोड से बहुत बेहतर हैं। असली सवाल अपवाद बनाम कथनों में है।

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

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

अभिकथन विफलताओं हमेशा घातक, अपरिवर्तनीय त्रुटियां हैं। स्मृति भ्रष्टाचार, उस तरह की बात।

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

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


वैकल्पिक रूप से एक साधारण इंट जरूरी नहीं है। मैं NSError के लिए कुछ का उपयोग करने की अधिक संभावना होगी जो मैं Obj-C से उपयोग किया जाता हूं।
प्रति जोहानसन

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

ऑब्जेक्टिव-सी वसीयत का उपयोग करने वाला कोई भी व्यक्ति आपको बताएगा कि आप पूरी तरह से गलत हैं।
gnasher729

5

अपवाद का आविष्कार एक कारण के लिए किया गया था, जो आपके सभी कोड को इस तरह देखने से बचने के लिए है:

bool success = function1(&result1, &err);
if (!success) {
    error_handler(err);
    return;
}

success = function2(&result2, &err);
if (!success) {
    error_handler(err);
    return;
}

इसके बजाय, आपको कुछ ऐसा मिलता है, जो निम्नलिखित की तरह दिखता है, एक अपवाद हैंडलर के साथ mainया अन्यथा आसानी से स्थित है:

result1 = function1();
result2 = function2();

कुछ लोग बिना किसी अपवाद के दृष्टिकोण के प्रदर्शन के लाभों का दावा करते हैं, लेकिन मेरी राय में पठनीयता मामूली प्रदर्शन लाभ से चिंतित है, खासकर जब आप सभी if (!success)बॉयलरप्लेट के निष्पादन का समय शामिल करते हैं, जिसे आपको हर जगह छिड़कना पड़ता है, या जब आप डॉन करते हैं तो सीगफुल को डिबग करने का जोखिम होता है ' t इसमें शामिल हैं, और अपवादों की संभावना पर विचार करना अपेक्षाकृत दुर्लभ है।

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


1
यह अधिक समझ में आता है अगर अपवाद के बिना कोड वास्तव में ऐसा दिखता है, लेकिन यह आमतौर पर नहीं होता है। यह पैटर्न तब दिखाई देता है जब error_handler कभी नहीं लौटता है, लेकिन शायद ही कभी।
प्रति जोहानसन

1

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

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

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

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

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


मैं आपसे सहमत हूं, लेकिन यह सबसे महत्वपूर्ण कारण है जिससे मुझे सवाल पूछने में कोई दिक्कत नहीं हुई।
प्रति जोहानसन

0

क्लीन कोड में चैप्टर की एक प्यारी जोड़ी है जो इस विषय के बारे में बात करती है - माइनस द एप्पल पॉइंट ऑफ़ व्यू।

मूल विचार यह है कि आप अपने कार्यों से कभी नहीं लौटते हैं, क्योंकि यह:

  • बहुत सारे डुप्लिकेट कोड जोड़ता है
  • आपके कोड की गड़बड़ी बनाता है - Ie: इसे पढ़ना कठिन हो जाता है
  • अक्सर शून्य-सूचक त्रुटियों के परिणामस्वरूप हो सकता है - या आपके विशेष प्रोग्रामिंग "धर्म" के आधार पर उल्लंघन का उपयोग

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

अपने COM विकास के दिनों में, मेरे पास एक ऐसी परियोजना थी जहां हर विफलता ने एक अपवाद को उठाया, और प्रत्येक अपवाद को विंडोज स्टैंडर्ड COM अपवादों के आधार पर एक अद्वितीय त्रुटि कोड आईडी का उपयोग करने की आवश्यकता थी। यह पहले के प्रोजेक्ट से एक होल्ड-ओवर था जहां पहले त्रुटि कोड का उपयोग किया गया था, लेकिन कंपनी सभी ऑब्जेक्ट-ओरिएंटेड जाना और COM-बैंडवागन पर कूदना चाहती थी, जिस तरह से उन्होंने चीजों को बहुत ज्यादा बदल दिया। उन्हें त्रुटि कोड संख्याओं से बाहर निकलने में केवल 4 महीने लगे और इसके बजाय अपवादों का उपयोग करने के लिए कोड के बड़े ट्रैक्ट को रिफलेक्टर करने के लिए मेरे पास गिर गया। अपने आप को सामने वाले को बचाने के लिए बेहतर है और केवल अपवादों का विवेकपूर्ण उपयोग करें।


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