सी ++ में अपवादों का मुहावरेदार उपयोग


16

Isocpp.org अपवाद अक्सर पूछे जाने वाले प्रश्न बताता है

किसी फ़ंक्शन के उपयोग में कोडिंग त्रुटि को इंगित करने के लिए थ्रो का उपयोग न करें। डीबगर में प्रक्रिया भेजने या प्रक्रिया को क्रैश करने के लिए या डेवलपर को डिबग करने के लिए क्रैश डंप को इकट्ठा करने के लिए एसर या अन्य तंत्र का उपयोग करें।

दूसरी ओर मानक पुस्तकालय std :: logic_error और यह सब व्युत्पन्न है, जो मुझे लगता है जैसे कि वे संभालने के लिए चाहिए, अन्य चीजों के अलावा, प्रोग्रामिंग त्रुटियों को परिभाषित करता है। क्या खाली स्ट्रिंग को पास करना std :: stof (अमान्य_argument फेंक देगा) प्रोग्रामिंग त्रुटि नहीं है? क्या एक स्ट्रिंग पास करना जिसमें '1' / '0' से भिन्न वर्ण एसटीडी तक हो: बिटसेट (अमान्य_गर्भास फेंक देगा) प्रोग्रामिंग त्रुटि नहीं है? क्या कॉलिंग स्टैड :: बिटसेट :: एक अमान्य इंडेक्स के साथ सेट किया गया है (आउट_ऑफ़_रेन्ज को फेंक देगा) प्रोग्रामिंग त्रुटि नहीं है? यदि ये नहीं हैं, तो प्रोग्रामिंग त्रुटि क्या है जिसके लिए कोई परीक्षण करेगा? Std :: बिटसेट स्ट्रिंग-आधारित कंस्ट्रक्टर केवल C ++ 11 के बाद से मौजूद है, इसलिए इसे अपवादों को ध्यान में रखते हुए उपयोग में लाया जाना चाहिए। दूसरी ओर मैंने लोगों को बताया है कि लॉजिक_रोर को मूल रूप से इस्तेमाल नहीं किया जाना चाहिए।

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

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


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

1
@RobertHarvey की परिभाषा में अभी भी वही समस्या है - अगर किसी चीज़ को मानवीय हस्तक्षेप के बिना हल किया जा सकता है या नहीं केवल एक कार्यक्रम की ऊपरी परतों के लिए जाना जाता है।
कुकीज

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

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

2
जो मेरे सिद्धांत को विश्वास दिलाता है कि ऐसा नियम वास्तव में मौजूद नहीं हो सकता है। मैंने C ++ लाउंज से कुछ लोगों को यह देखने के लिए आमंत्रित किया है कि क्या वे आपके प्रश्न का उत्तर दे सकते हैं, हालांकि हर बार जब मैं वहां जाता हूं, तो उनकी सलाह है "C ++ का उपयोग बंद करो, यह आपके मस्तिष्क को भून देगा।" इसलिए उनकी सलाह को अपने जोखिम पर लें।
रॉबर्ट हार्वे

जवाबों:


15

सबसे पहले, मैं यह इंगित करने के लिए बाध्य हूं कि बहुत पहले std::exceptionऔर उसके बच्चों को डिजाइन किया गया था। वहाँ भागों की एक संख्या है कि शायद (लगभग निश्चित रूप से) अलग होगा अगर वे आज डिजाइन किए जा रहे थे।

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

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

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

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

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

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

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


9

Std :: बिटसेट स्ट्रिंग-आधारित कंस्ट्रक्टर केवल C ++ 11 के बाद से मौजूद है, इसलिए इसे अपवादों को ध्यान में रखते हुए उपयोग में लाया जाना चाहिए। दूसरी तरफ मैंने लोगों को बताया है कि लॉजिक_रेर को मूल रूप से इस्तेमाल नहीं किया जाना चाहिए।

आप इस पर विश्वास नहीं कर सकते हैं, लेकिन, ठीक है, विभिन्न सी ++ कोडर्स असहमत हैं। इसलिए एफएक्यू एक बात कहता है लेकिन मानक पुस्तकालय असहमत है।

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

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

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

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

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

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

आप अपवाद फेंक सकते हैं:

  1. कभी नहीँ
  2. हर जगह
  3. केवल प्रोग्रामर त्रुटियों पर
  4. प्रोग्रामर त्रुटियों पर कभी नहीं
  5. केवल गैर-दिनचर्या (असाधारण) विफलताओं के दौरान

और इंटरनेट पर कोई ऐसा व्यक्ति खोजें जो आपसे सहमत हो। आपको उस शैली को अपनाना होगा जो आपके लिए काम करती है।


यह संभवतः ध्यान देने योग्य है कि केवल अपवादों का उपयोग करने का सुझाव जब परिस्थितियां वास्तव में असाधारण होती हैं, तो उन लोगों द्वारा व्यापक रूप से प्रचारित किया जाता है जो उन भाषाओं के बारे में पढ़ाते हैं जहां अपवादों का प्रदर्शन खराब है। C ++ उन भाषाओं में से एक नहीं है।
जूल्स

1
@ जूल्स - अब वह (प्रदर्शन) निश्चित रूप से एक खुद के जवाब के हकदार हैं जहां आप अपने दावे का समर्थन करते हैं। C ++ अपवाद प्रदर्शन है निश्चित रूप से एक मुद्दा है, और अधिक, शायद कहीं और की तुलना में कम है, लेकिन उन्होंने कहा कि "सी ++ उन भाषाओं [जहां अपवाद खराब प्रदर्शन है] से एक नहीं है" निश्चित रूप से बहस का मुद्दा है हो सकता है।
मार्टिन बा

1
@MartinBa -, Java, C ++ अपवाद प्रदर्शन की तुलना में, परिमाण के क्रम में तेज़ी है। बेंचमार्क का सुझाव है कि अपवाद को 1 स्तर फेंकने का प्रदर्शन C ++ में रिटर्न मान को संभालने की तुलना में लगभग 50x धीमा है, जावा में 1000x से अधिक धीमी है। इस मामले में जावा के लिए लिखी गई सलाह को अतिरिक्त विचार के बिना C ++ पर लागू नहीं किया जाना चाहिए क्योंकि दोनों के बीच प्रदर्शन में परिमाण के अंतर से अधिक है। शायद मुझे "खराब प्रदर्शन" के बजाय "बेहद खराब प्रदर्शन" लिखना चाहिए था।
जूल्स

1
@ जूल्स - इन नंबरों के लिए धन्यवाद। (किसी भी स्रोत?) मैं उन पर विश्वास कर सकता हूं, क्योंकि जावा (और सी #) को स्टैक ट्रेस पर कब्जा करने की आवश्यकता है, जो निश्चित रूप से ऐसा लगता है कि यह वास्तव में महंगा हो सकता है। मैं अभी भी आपको लगता है कि प्रारंभिक प्रतिक्रिया थोड़े भ्रामक है, क्योंकि यहां तक ​​कि एक 50x मंदी बहुत भारी है मुझे लगता है, esp। C ++ जैसी प्रदर्शन उन्मुख भाषा में।
मार्टिन बा

2

कई अन्य अच्छे उत्तर लिखे गए हैं, मैं केवल एक संक्षिप्त बिंदु जोड़ना चाहता हूं।

पारंपरिक जवाब, खासकर जब आईएसओ सी ++ एफएक्यू लिखा गया था, मुख्य रूप से "सी ++ अपवाद" बनाम "सी-स्टाइल रिटर्न कोड" की तुलना करता है। एक तीसरा विकल्प, "समग्र मूल्य के कुछ प्रकार वापसी, जैसे एक structया union, या आजकल, boost::variantया (प्रस्तावित) std::expected, नहीं माना जाता।

C ++ 11 से पहले "वापसी एक समग्र प्रकार" विकल्प आमतौर पर बहुत कमजोर था। क्योंकि, कोई गति शब्दार्थ नहीं था इसलिए किसी संरचना में और बाहर चीजों की नकल करना संभवतः बहुत महंगा था। सबसे अच्छा प्रदर्शन पाने के लिए आरवीओ की ओर अपने कोड को स्टाइल करने के लिए भाषा में उस बिंदु पर यह बेहद महत्वपूर्ण था । अपवाद एक प्रभावी तरीके से एक समग्र प्रकार को वापस करने के लिए एक आसान तरीका था, जब अन्यथा यह काफी मुश्किल होगा।

IMO, C ++ 11 के बाद, यह विकल्प "एक विभेदित संघ लौटाएं", Result<T, E>आजकल रुस्ट में उपयोग किए जाने वाले मुहावरे के समान , C ++ कोड में अधिक बार इष्ट होना चाहिए। कभी-कभी यह वास्तव में त्रुटियों को इंगित करने की एक सरल और अधिक सुविधाजनक शैली है। अपवादों के साथ, इस तरह की संभावना हमेशा होती है कि फ़ंक्शंस के बाद फेंकने वाले कार्य अचानक शुरू नहीं हो सकते हैं, और प्रोग्रामर हमेशा ऐसे सामान को अच्छी तरह से दस्तावेज नहीं करते हैं। जब त्रुटि को एक विभेदित संघ में वापसी मूल्य के हिस्से के रूप में इंगित किया जाता है, तो यह इस संभावना को बहुत कम कर देता है कि प्रोग्रामर बस त्रुटि कोड को अनदेखा करेगा, जो सी-स्टाइल त्रुटि हैंडलिंग की सामान्य आलोचना है।

आमतौर पर Result<T, E>वैकल्पिक को बढ़ावा देने की तरह काम करता है। आप परीक्षण कर सकते हैं, का उपयोग कर operator bool, अगर यह एक मूल्य या एक त्रुटि है। और फिर operator *मान का उपयोग करने के लिए कहते हैं , या कुछ अन्य "प्राप्त" फ़ंक्शन। आमतौर पर वह पहुंच अनियंत्रित होती है, गति के लिए। लेकिन आप इसे बना सकते हैं ताकि डिबग बिल्ड में, एक्सेस की जाँच हो जाए और यह सुनिश्चित हो जाए कि वास्तव में एक मूल्य है और त्रुटि नहीं है। इस तरह जो कोई भी त्रुटियों के लिए ठीक से जांच नहीं करता है, उसे कुछ अधिक कठिन समस्या के बजाय एक कठिन मुखर मिलेगा।

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

मैं इस शैली का बहुत बड़ा प्रशंसक बन गया हूं। आमतौर पर, मैं आजकल या तो इस या अपवादों का उपयोग करता हूं। लेकिन मैं अपवादों को प्रमुख समस्याओं तक सीमित रखने की कोशिश करता हूं। किसी पार्स त्रुटि की तरह कुछ के लिए, मैं expected<T>उदाहरण के लिए वापस जाने की कोशिश करता हूं । कुछ अपेक्षाकृत छोटी समस्या "स्ट्रिंग को संख्या में परिवर्तित नहीं किया जा सकता है" की स्थिति में सी ++ अपवाद की तरह std::stoiऔर boost::lexical_castजो फेंकते हैं वह आजकल मुझे बहुत खराब स्वाद में लगते हैं।


1
std::expectedअभी भी एक गैर-स्वीकृत प्रस्ताव है, है ना?
मार्टिन बा

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

आंद्रेई अलेक्जेंड्रेस्कु की बात यहां है: Channel9.msdn.com/Shows/Going+Deep/… वह विस्तार से दिखाता है कि इस तरह से एक वर्ग कैसे बनाया जाए और आपके पास क्या विचार हो सकते हैं।
बेक पर क्रिस बेक

प्रस्तावित [[nodiscard]] attributeइस त्रुटि से निपटने के दृष्टिकोण के लिए सहायक होगा क्योंकि यह सुनिश्चित करता है कि आप गलती से त्रुटि परिणाम को अनदेखा न करें।
कोडइन्चोस 10

- हाँ, मैं एए की बात जानता था। मुझे डिजाइन बहुत अजीब लगा, क्योंकि इसे अनपैक करना ( except_ptr) आपको आंतरिक रूप से एक अपवाद फेंकना था। व्यक्तिगत रूप से मुझे लगता है कि इस तरह के उपकरण को निष्पादन के लिए पूरी तरह से स्वतंत्र होना चाहिए। सिर्फ एक टिप्पणी।
Ba पर मार्टिन बा

1

यह एक अत्यधिक व्यक्तिपरक मुद्दा है, क्योंकि यह डिजाइन का हिस्सा है। और क्योंकि डिजाइन मूल रूप से कला है, मैं बहस के बजाय इन चीजों पर चर्चा करना पसंद करता हूं (मैं यह नहीं कह रहा हूं कि आप बहस कर रहे हैं)।

मेरे लिए, असाधारण मामले दो प्रकार के होते हैं - वे जो संसाधनों से संबंधित होते हैं और वे जो महत्वपूर्ण कार्यों से निपटते हैं। जिसे महत्वपूर्ण माना जा सकता है वह हाथ में समस्या पर निर्भर करता है, और, कई मामलों में, प्रोग्रामर के दृष्टिकोण पर।

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

फिर संसाधनों को अपडेट करना आता है। यह बिंदु मेरे लिए कम से कम, आवेदन के महत्वपूर्ण संचालन पहलू से निकटता से संबंधित है। Employeeएक फ़ंक्शन के साथ एक वर्ग की कल्पना करें UpdateDetails(std::string&)जो दिए गए अल्पविराम से अलग किए गए स्ट्रिंग के आधार पर विवरण को संशोधित करता है। मेमोरी फ़ेल होने की रिलीज़ के समान, मुझे ऐसे डोमेन में मेरे अनुभव की कमी के कारण असफल होने वाले सदस्य चर मानों के असाइनमेंट की कल्पना करना कठिन लगता है, जहां ये हो सकता है। हालाँकि, एक फ़ंक्शन UpdateDetailsAndUpdateFile(std::string&)जैसा कि नाम इंगित करता है कि विफल होने की उम्मीद है। इसे मैं क्रिटिकल ऑपरेशन कहता हूं।

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

जाहिर है, कई महत्वपूर्ण ऑपरेशन हैं जो संसाधन से बंधे नहीं हैं। यदि UpdateDetails()गलत डेटा दिया जाता है, तो यह विवरण को अपडेट नहीं करेगा और विफलता को ज्ञात किया जाना चाहिए, इसलिए आप यहां एक अपवाद फेंक देंगे। लेकिन जैसे फंक्शन की कल्पना करो GiveRaise()। अब, यदि उक्त कर्मचारी एक भाग्यशाली बालों वाले मालिक के लिए भाग्यशाली है और उसे एक वृद्धि नहीं मिलेगी (प्रोग्रामिंग शब्दों में, कुछ चर का मूल्य ऐसा होने से रोकता है), फ़ंक्शन अनिवार्य रूप से विफल हो गया है। क्या आप यहां अपवाद फेंकेंगे? मैं जो कह रहा हूं वह आपको एक अपवाद की आवश्यकता का मूल्यांकन करना है।

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

मैं ऐसे कई लोगों को देखता हूं जो मूल रूप से अपवादों के साथ वापसी मूल्यों को प्रतिस्थापित करते हैं क्योंकि वे C ++ और C का उपयोग नहीं कर रहे हैं, और यह कि 'त्रुटि से निपटने का अच्छा जुदाई' आदि देता है और मुझे 'मिश्रण' भाषाओं आदि को रोकने के लिए आग्रह करता हूं जो मैं आमतौर पर दूर रहता हूं। ऐसे लोग।


1

सबसे पहले, के रूप में दूसरों ने कहा है, बातें नहीं कर रहे हैं कि सी में स्पष्ट कटौती ++, IMHO ज्यादातर क्योंकि आवश्यकताओं और मजबूरी कुछ और अधिक सी में विविध रहे हैं ++ अन्य भाषाओं की तुलना में, esp। सी # और जावा, जिसमें "समान" अपवाद मुद्दे हैं।

मैं std पर प्रकट करता हूँ :: stof उदाहरण:

std :: stof (अमान्य_argument फेंक देगा) के लिए एक रिक्त स्ट्रिंग पास करना एक प्रोग्रामिंग त्रुटि नहीं है

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

यहाँ, एक कह सकता है कि logic_errorयह इंगित करने के लिए उपयोग किया जाता है कि, (रनटाइम) इनपुट को देखते हुए, इसे बदलने की कोशिश करने के लिए हमेशा एक त्रुटि होती है - लेकिन यह निर्धारित करने और आपको बताने के लिए फ़ंक्शन का काम है (अपवाद के माध्यम से)।

साइड नोट: उस दृश्य में, एक चीज़ के रूप में देखा runtime_error जा सकता है, जो किसी फ़ंक्शन को एक ही इनपुट देता है, सैद्धांतिक रूप से अलग-अलग रनों के लिए सफल हो सकता है। (जैसे एक फ़ाइल ऑपरेशन, DB पहुँच, आदि)

आगे की ओर ध्यान दें: C ++ रेगेक्स लाइब्रेरी ने इसे प्राप्त करने के लिए चुना , runtime_errorहालांकि इसमें ऐसे मामले हैं जहां इसे (अमान्य रेगेक्स पैटर्न) के रूप में वर्गीकृत किया जा सकता है।

यह सिर्फ दिखाता है, IMHO, कि या त्रुटि में समूहीकरण सी ++ में बहुत फजी हैlogic_runtime_ और वास्तव में सामान्य मामले में ज्यादा मदद नहीं करता है (*) - यदि आपको विशिष्ट त्रुटियों को संभालने की आवश्यकता है, तो आपको संभवतः दोनों की तुलना में कम पकड़ने की आवश्यकता है।

(*): यह कहना है कि कोड के एक टुकड़े के अनुरूप नहीं होना चाहिए नहीं है, लेकिन है कि क्या आप फेंक runtime_या logic_या custom_somethings वास्तव में है कि महत्वपूर्ण है, मुझे लगता है कि नहीं।


दोनों पर टिप्पणी करने के लिए stofऔर bitset:

दोनों कार्य तर्क के रूप में तार लेते हैं , और दोनों मामलों में यह है:

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

नियम जो अपवादों के साथ अक्सर आता है, "असाधारण परिस्थितियों में केवल अपवादों का उपयोग करें"। लेकिन एक लाइब्रेरी फ़ंक्शन को कैसे पता होना चाहिए कि कौन सी परिस्थितियां असाधारण हैं?

यह कथन, IMHO, दो जड़ें हैं:

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

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

उदाहरण:

float readOrDefault;
try {
  readOrDefault = stof(...);
} catch(std::exception&) {
  // discard execption, just use default value
  readOrDefault = 3.14f; // 3.14 is the default value if cannot be read
}

यहां ऐसे कार्य किए गए हैं जैसे TryParseबनाम Parseखेलने में आते हैं: जब स्थानीय कोड के लिए एक संस्करण पार्स स्ट्रिंग को मान्य होने की उम्मीद करता है, तो एक संस्करण जब स्थानीय कोड मानता है कि यह वास्तव में अपेक्षित है (यानी गैर असाधारण) जो पार्सिंग विफल हो जाएगा।

वास्तव में, stofबस (एक के रूप में परिभाषित) चारों ओर एक आवरण strtof, इसलिए यदि आप अपवाद नहीं चाहते हैं, तो उस एक का उपयोग करें।


इसलिए, मुझे यह कैसे तय करना चाहिए कि मुझे किसी विशेष कार्य के लिए अपवादों का उपयोग करना चाहिए या नहीं?

IMHO, आपके पास दो मामले हैं:

  • फ़ंक्शन की तरह "लाइब्रेरी" (विभिन्न संदर्भों में अक्सर उपयोग किया जाता है): आप मूल रूप से तय नहीं कर सकते। संभवतः दोनों संस्करण प्रदान करते हैं, हो सकता है कि एक त्रुटि की रिपोर्ट करता है और एक आवरण जो एक अपवाद में लौटे त्रुटि को परिवर्तित करता है।

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

बेशक वहाँ बीच में जगह होगी - बस का उपयोग करें क्या जरूरत है और याद रखें YAGNI।


अंतिम, मुझे लगता है कि मुझे अक्सर पूछे जाने वाले प्रश्न पर वापस जाना चाहिए,

किसी फ़ंक्शन के उपयोग में कोडिंग त्रुटि को इंगित करने के लिए थ्रो का उपयोग न करें। इस प्रक्रिया को डिबगर में भेजने के लिए या प्रक्रिया को क्रैश करने के लिए अभिकारक या अन्य तंत्र का उपयोग करें ...

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

लेकिन जब यह उपयुक्त होता है तो अक्सर अत्यधिक विशिष्ट होता है, इसलिए लाइब्रेरी डोमेन बनाम एप्लिकेशन डोमेन के ऊपर देखें।

यह इस सवाल पर वापस आता है कि कॉलिंग पूर्व शर्त को कैसे और कैसे मान्य किया जाए , लेकिन मैं इसमें नहीं जाऊंगा, उत्तर पहले से ही बहुत लंबा है :)

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