क्या मुझे std :: अपवाद से विरासत में मिलना चाहिए?


98

मैंने कम से कम एक विश्वसनीय स्रोत (एक C ++ वर्ग जो मैंने लिया है) देखा है कि C ++ में एप्लिकेशन-विशिष्ट अपवाद वर्गों से इनहेरिट करना चाहिए std::exception। मैं इस दृष्टिकोण के लाभों पर स्पष्ट नहीं हूं।

C # से विरासत में मिलने के कारण ApplicationExceptionस्पष्ट हैं: आपको कुछ उपयोगी तरीके, गुण और निर्माता मिलते हैं और आपको जो कुछ भी चाहिए उसे जोड़ना या ओवरराइड करना होगा। इसके साथ std::exceptionऐसा लगता है कि आपको मिलने वाला सब कुछ what()ओवरराइड करने की एक विधि है, जिसे आप बस खुद बना सकते हैं।

तो std::exceptionमेरे आवेदन-विशिष्ट अपवाद वर्ग के लिए आधार वर्ग के रूप में उपयोग करने पर क्या लाभ हैं, यदि कोई हो ? क्या कोई अच्छे कारण से विरासत में नहीं हैं std::exception?


आप इस पर एक नज़र रखना चाह सकते हैं: stackoverflow.com/questions/1605778/1605852#1605852
sbi

2
हालांकि एक पक्ष नोट विशेष प्रश्न से असंबंधित है, C ++ कक्षाएं जो आपको लेने की आवश्यकता है, बस अपने स्वयं के अधिकार से बाहर अच्छी प्रथाओं पर विश्वसनीय स्रोत नहीं होना चाहिए।
क्रिश्चियन राऊ

जवाबों:


69

मुख्य लाभ अपनी कक्षाओं का उपयोग कर आप क्या का सही प्रकार पता करने के लिए नहीं है कि कोड है throwउस पर, लेकिन सिर्फ कर सकते हैं । संपादित करें: जैसा कि मार्टिन और अन्य ने उल्लेख किया है, आप वास्तव में हेडर में घोषित उप-वर्गों में से एक से प्राप्त करना चाहते हैं ।catchstd::exception

std::exception<stdexcept>


21
Std :: अपवाद के लिए एक संदेश पास करने का कोई तरीका नहीं है। std :: runtime_error एक स्ट्रिंग को स्वीकार करता है और इसे std :: अपवाद से लिया जाता है।
मार्टिन यॉर्क

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

std::exceptionहै परिभाषित में <exception>हैडर ( सबूत )। -1
अलेक्जेंडर शुकेव

5
तो आपकी बात क्या है? परिभाषा का तात्पर्य घोषणा है, आप यहाँ क्या गलत देख रहे हैं?
निकोलाई फेटिसोव

40

इसके साथ समस्या std::exceptionयह है कि कोई रचनाकार नहीं है (मानक अनुरूप संस्करणों में) जो एक संदेश को स्वीकार करता है।

परिणामस्वरूप मैं इससे प्राप्त करना पसंद करता हूं std::runtime_error। यह व्युत्पन्न है, std::exceptionलेकिन इसके निर्माणकर्ता आपको सी-स्ट्रिंग या std::stringउस निर्माणकर्ता को पास करने की अनुमति देते हैं जिसे वापस बुलाया जाएगा (ए char const*) जब what()कहा जाता है।


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

16
@ एमिल: सुनिश्चित करें कि यदि आप अपवाद हैं तो उपयोगकर्ता प्रदर्शन योग्य संदेश ले जा सकते हैं, हालांकि आम तौर पर वे केवल लॉगिंग उद्देश्यों के लिए होते हैं। फेंक साइट पर आपके पास उपयोगकर्ता संदेश बनाने का संदर्भ नहीं है (जैसा कि यह वैसे भी एक पुस्तकालय हो सकता है)। कैच साइट में अधिक संदर्भ होगा और उपयुक्त संदेश उत्पन्न कर सकता है।
मार्टिन यॉर्क

3
मुझे नहीं पता कि आपको यह विचार कहां से आया कि अपवाद केवल लॉगिंग उद्देश्यों के लिए हैं। :)
एमिल

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

1
@Emil। यह चर्चा एक साल पुरानी है। इस बिंदु पर अपना प्रश्न बनाएं। जहाँ तक मेरा तर्क है आप गलत हैं (और वोटों के आधार पर लोग मुझसे सहमत होते हैं)। देखें कि मेरे पुस्तकालय को लागू करने के लिए अपवादों की 'अच्छी संख्या' क्या है? और सामान्य अपवादों को पकड़ना वास्तव में एक बुरी बात है? आपने अपनी पिछली डायरी के बाद से कोई nw जानकारी नहीं दी है।
मार्टिन यॉर्क

17

इनसे विरासत में लेने std::exceptionका कारण अपवादों के लिए "मानक" आधार वर्ग है, इसलिए यह एक टीम के अन्य लोगों के लिए स्वाभाविक है, उदाहरण के लिए, यह अपेक्षा करना और आधार पकड़ना std::exception

यदि आप सुविधा की तलाश कर रहे हैं, तो आप std::runtime_errorउस से विरासत में प्राप्त कर सकते हैं जो std::stringकंस्ट्रक्टर प्रदान करता है ।


2
Std :: अपवाद को छोड़कर किसी भी अन्य मानक अपवाद प्रकार से व्युत्पन्न करना एक अच्छा विचार नहीं है। समस्या यह है कि वे std से व्युत्पन्न हैं :: अपवाद गैर-वस्तुतः, जो सूक्ष्म वंशानुक्रम को एकाधिक वंशानुक्रम से युक्त कर सकता है जहाँ एक पकड़ (std :: अपवाद &) std में रूपांतरण के कारण अपवाद को पकड़ने में विफल हो सकता है :: अपवाद अस्पष्ट।
एमिल

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

5
@ ईमिल: इसके अलावा अन्य मानक अपवादों से व्युत्पन्न std::exceptionएक उत्कृष्ट विचार है। आपको जो नहीं करना चाहिए वह एक से अधिक से विरासत में मिला है ।
मूविंग डक

@MooDDuck: यदि आप एक से अधिक मानक अपवाद प्रकार से प्राप्त करते हैं, तो आप कई बार std :: अपवाद (गैर-वस्तुतः) से प्राप्त करेंगे। Boost.org/doc/libs/release/libs/exception/doc/… देखें ।
एमिल

1
@ ईमिल: जो मैंने कहा था, वह इस प्रकार है।
मिंग डक

13

मैंने एक बार एक बड़े कोडबेस की सफाई में भाग लिया था जहाँ पिछले लेखकों ने चींटियों, HRESULTS, std :: string, char *, random classes ... हर जगह अलग-अलग सामान फेंके थे; बस एक प्रकार का नाम और यह शायद कहीं फेंक दिया गया था। और कोई सामान्य आधार वर्ग नहीं है। मेरा विश्वास करो, एक बार जब हम इस बात पर पहुंच गए थे कि सभी फेंके गए प्रकारों का एक सामान्य आधार था जिसे हम पकड़ सकते हैं और जानते हैं कि अतीत में कुछ भी नहीं होने वाला था। तो कृपया अपने आप को (और जिन्हें भविष्य में आपके कोड को बनाए रखना होगा) एक एहसान करें और इसे शुरू से ही इस तरह से करें।


12

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


5
हालाँकि ध्यान दें कि बढ़ावा :: अपवाद स्वयं std से नहीं मिलता है :: अपवाद। यहां तक ​​कि जब बढ़ावा देने के अपवाद :: अपवाद से, आपको std से भी प्राप्त करना चाहिए :: अपवाद (एक अलग नोट के रूप में, जब भी अपवाद प्रकारों से व्युत्पन्न होता है, तो आपको वर्चुअल विरासत का उपयोग करना चाहिए।)
एमिल

10

हाँ आप से प्राप्त करना चाहिए std::exception

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


6
+1, अच्छी बात है। चिंताओं की जुदाई और एक i18n परिप्रेक्ष्य से दोनों, प्रस्तुति परत को उपयोगकर्ता संदेश बनाने देना निश्चित रूप से बेहतर है।
जॉन एम गैंट

@ जॉनसन और एमिल: दिलचस्प है, क्या आप इस पर एक ठोस उदाहरण इंगित कर सकते हैं कि यह कैसे किया जा सकता है। मैं समझता हूं कि कोई भी std::exceptionअपवाद की जानकारी प्राप्त कर सकता है। लेकिन त्रुटि संदेश के निर्माण / प्रारूप के लिए कौन जिम्मेदार होगा? ::what()समारोह या कुछ और?
alfC

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

9

इस कारण से कि आप किस से विरासत में प्राप्त करना चाहते हैं std::exception, क्योंकि यह आपको एक अपवाद फेंकने की अनुमति देता है जो उस वर्ग के अनुसार पकड़ा जाता है, अर्थात:

class myException : public std::exception { ... };
try {
    ...
    throw myException();
}
catch (std::exception &theException) {
    ...
}

6

विरासत के साथ एक समस्या है जिसके बारे में आपको पता होना चाहिए कि वस्तु स्लाइसिंग है। जब आप throw e;थ्रो-एक्सप्रेशन लिखते हैं तो एक अस्थायी ऑब्जेक्ट को इनिशियलाइज़ करता है, अपवाद ऑब्जेक्ट कहलाता है, जिसके प्रकार को किसी भी टॉप-लेवल cv-क्वालिफायर के स्टैटिक प्रकार से हटाकर निर्धारित किया जाता है throw। वह नहीं हो सकता है जो आप उम्मीद कर रहे हैं। समस्या का उदाहरण आपको यहां मिल सकता है

यह विरासत के खिलाफ एक तर्क नहीं है, यह सिर्फ 'पता होना चाहिए' जानकारी है।


3
मुझे लगता है कि दूर ले जाना है कि "फेंक ई?" बुराई है, और "फेंक;" ठीक है।
जेम्स स्कैच

2
हां, throw;यह ठीक है, लेकिन यह स्पष्ट नहीं है कि आपको ऐसा कुछ लिखना चाहिए।
किरिल वी। ल्याडविंस्की

1
यह जावा डेवलपर्स के लिए विशेष रूप से दर्दनाक है जहां रीथ्रो "फेंक ई;" का उपयोग करके किया जाता है
जेम्स स्कैच

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

आप इस समस्या को किसी सदस्य फ़ंक्शन को जोड़कर भी हल कर सकते हैं raise(), जैसे virtual void raise() { throw *this; }:। बस इसे प्रत्येक व्युत्पन्न अपवाद में ओवरराइड करना याद रखें।
जस्टिन टाइम -

5

अंतर: std :: runtime_error बनाम std :: अपवाद ()

आप चाहे चाहिए यह से विरासत या नहीं आप पर निर्भर है। मानक std::exceptionऔर उसके मानक वंशज एक संभावित अपवाद पदानुक्रम संरचना ( logic_errorसबहियरार्की और runtime_errorसबहार्की में विभाजन ) और एक संभावित अपवाद ऑब्जेक्ट इंटरफ़ेस का प्रस्ताव करते हैं । यदि आप इसे पसंद करते हैं - इसका उपयोग करें। यदि किसी कारण से आपको कुछ अलग करने की आवश्यकता है - अपने स्वयं के अपवाद ढांचे को परिभाषित करें।


3

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

दूसरे शब्दों में, इसके या तो

catch (...) {cout << "Unknown exception"; }

या

catch (const std::exception &e) { cout << "unexpected exception " << e.what();}

और दूसरा विकल्प निश्चित रूप से बेहतर है।


3

यदि आपके सभी संभावित अपवाद हैं std::exception, तो आपका कैच ब्लॉक सरल catch(std::exception & e)हो सकता है और हर चीज पर कब्जा करने का आश्वासन दिया जा सकता है।

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


7
एसटीडीसी उप-अपवाद न करें: अपवाद और फिर कैच (एसटी :: अपवाद ई)। आपको std :: अपवाद और (या एक पॉइंटर) के संदर्भ को पकड़ने की आवश्यकता है या फिर आप उपवर्ग डेटा को बंद कर रहे हैं।
jmucchiello

1
अब वह एक गंभीर गलती थी - निश्चित रूप से मैं बेहतर जानता हूं। stackoverflow.com/questions/1095225/…
मार्क रैनसम

3

किसी भी मानक अपवाद प्रकार से प्राप्त करना है या नहीं यह पहला सवाल है। ऐसा करना सभी मानक पुस्तकालय अपवादों और अपने स्वयं के लिए एक एकल अपवाद हैंडलर को सक्षम बनाता है, लेकिन यह ऐसे कैच-उन-सभी हैंडलर को भी प्रोत्साहित करता है। समस्या यह है कि किसी को केवल अपवादों को पकड़ना चाहिए, एक को संभालना जानता है। मुख्य () में, उदाहरण के लिए, सभी std :: अपवादों को पकड़ना संभवत: अच्छी बात है यदि बाहर निकलने से पहले क्या () स्ट्रिंग को अंतिम उपाय के रूप में लॉग किया जाएगा। हालांकि, यह एक अच्छा विचार होने की संभावना नहीं है।

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


0

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

उदा। एक घातक अपवाद कार्यक्रम को समाप्त कर देगा, एक सत्यापन त्रुटि केवल स्टैक को साफ कर देगी और एक उपयोगकर्ता क्वेरी अंतिम उपयोगकर्ता से एक प्रश्न पूछेगा।

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


0

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

का उपयोग करें std::nested_exceptionऔरstd::throw_with_nested

यह StackOverflow पर यहाँ और यहाँ वर्णित है , कि कैसे आप एक डिबगर या बोझिल लॉगिंग की आवश्यकता के बिना अपने कोड के अंदर अपने अपवादों पर एक बैकट्रेस प्राप्त कर सकते हैं , बस एक उचित अपवाद हैंडलर लिखकर जो अपवादों को हटा देगा।

चूंकि आप इसे किसी भी व्युत्पन्न अपवाद वर्ग के साथ कर सकते हैं, इसलिए आप इस तरह के बैकट्रेस में बहुत सारी जानकारी जोड़ सकते हैं! आप GitHub पर मेरे MWE पर एक नज़र डाल सकते हैं , जहाँ एक बैकट्रेस कुछ इस तरह दिखेगी:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

std::runtime_errorजब अपवाद को फेंक दिया जाता है तो आपको बहुत सारी जानकारी प्राप्त करने के लिए उप-वर्ग की आवश्यकता नहीं होती है।

उपवर्ग (केवल उपयोग करने के बजाय std::runtime_error) में मुझे जो एकमात्र लाभ दिखाई देता है, वह यह है कि आपका अपवाद हैंडलर आपके कस्टम अपवाद को पकड़ सकता है और कुछ विशेष कर सकता है। उदाहरण के लिए:

try
{
  // something that may throw
}
catch( const MyException & ex )
{
  // do something specialized with the
  // additional info inside MyException
}
catch( const std::exception & ex )
{
  std::cerr << ex.what() << std::endl;
}
catch( ... )
{
  std::cerr << "unknown exception!" << std::endl;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.