क्या मुझे C ++ में एक अपवाद विशेषांक का उपयोग करना चाहिए?


123

C ++ में, आप यह निर्दिष्ट कर सकते हैं कि कोई फ़ंक्शन एक अपवाद विशेषांक का उपयोग करके एक अपवाद को फेंक सकता है या नहीं फेंक सकता है। उदाहरण के लिए:

void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type

मैं निम्नलिखित के कारण वास्तव में उनका उपयोग करने के बारे में संदिग्ध हूं:

  1. कंपाइलर वास्तव में किसी भी कठोर तरीके से अपवाद निर्दिष्ट करने वालों को लागू नहीं करता है, इसलिए लाभ महान नहीं हैं। आदर्श रूप से, आप एक संकलन त्रुटि प्राप्त करना चाहेंगे।
  2. यदि कोई फ़ंक्शन किसी अपवाद विनिर्देशक का उल्लंघन करता है, तो मुझे लगता है कि मानक व्यवहार कार्यक्रम को समाप्त करना है।
  3. VS.Net में, यह थ्रो (X) को थ्रो (...) के रूप में मानता है, इसलिए मानक का पालन मजबूत नहीं है।

क्या आपको लगता है कि अपवाद विनिर्देशक का उपयोग किया जाना चाहिए?
कृपया "हां" या "नहीं" के साथ उत्तर दें और अपने उत्तर को सही ठहराने के लिए कुछ कारण प्रदान करें।


7
"फेंक (...)" मानक C ++ नहीं है। मेरा मानना ​​है कि यह कुछ संकलकों में एक विस्तार है और आम तौर पर इसका कोई अपवाद विनिर्देश के समान अर्थ है।
रिचर्ड कॉर्डन

जवाबों:


97

नहीं।

यहाँ कई उदाहरण हैं क्यों:

  1. खाका कोड अपवाद विनिर्देशों के साथ लिखना असंभव है,

    template<class T>
    void f( T k )
    {
         T x( k );
         x.x();
    }

    प्रतियां फेंक सकते हैं, गुजरने वाले पैरामीटर फेंक सकते हैं, और x()कुछ अज्ञात अपवाद फेंक सकते हैं।

  2. अपवाद-विशिष्टताओं में अत्यधिकता का निषेध होता है।

    virtual void open() throw( FileNotFound );

    में विकसित हो सकता है

    virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );

    आप वास्तव में ऐसा लिख ​​सकते हैं

    throw( ... )

    पहला एक्स्टेंसिबल नहीं है, दूसरा अत्यधिक है और तीसरा वास्तव में आपका मतलब है, जब आप वर्चुअल फ़ंक्शन लिखते हैं।

  3. लीगेसी कोड

    जब आप कोड लिखते हैं जो किसी अन्य पुस्तकालय पर निर्भर करता है, तो आप वास्तव में नहीं जानते कि जब कुछ बुरी तरह से गलत हो जाता है तो वह क्या कर सकता है।

    int lib_f();
    
    void g() throw( k_too_small_exception )
    { 
       int k = lib_f();
       if( k < 0 ) throw k_too_small_exception();
    }

    gसमाप्त हो जाएगा, जब lib_f()फेंकता है। यह (ज्यादातर मामलों में) वह नहीं है जो आप वास्तव में चाहते हैं। std::terminate()कभी नहीं बुलाया जाना चाहिए। हमेशा एक अनछुए अपवाद के साथ एप्लिकेशन को क्रैश करने देना बेहतर होता है, जिसमें से आप चुपचाप / हिंसक तरीके से मरने के बजाय स्टैक-ट्रेस प्राप्त कर सकते हैं।

  4. कोड लिखें जो सामान्य त्रुटियों को लौटाता है और असाधारण अवसरों पर फेंकता है।

    Error e = open( "bla.txt" );
    if( e == FileNotFound )
        MessageUser( "File bla.txt not found" );
    if( e == AccessDenied )
        MessageUser( "Failed to open bla.txt, because we don't have read rights ..." );
    if( e != Success )
        MessageUser( "Failed due to some other error, error code = " + itoa( e ) );
    
    try
    {
       std::vector<TObj> k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    { 
       MessageUser( "out of memory, exiting process" );
       throw;
    }

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


1
3 में, यह तकनीकी रूप से std :: अप्रत्याशित नहीं std :: समाप्त हो जाएगा। लेकिन जब इस फ़ंक्शन को कॉल किया जाता है, तो डिफ़ॉल्ट एबॉर्ट्स () को आमंत्रित करता है। यह एक कोर डंप उत्पन्न करता है। यह एक अखंड अपवाद से भी बदतर कैसे है? (जो मूल रूप से एक ही बात करता है)
ग्रेग रोजर्स

6
@Greg रोजर्स: एक अनियंत्रित अपवाद अभी भी स्टैक अनइंडिंग करता है। इसका मतलब है कि विध्वंसक कहलाएंगे। और उन विध्वंसक में, बहुत कुछ किया जा सकता है, जैसे: संसाधन सही ढंग से मुक्त हो गए, लॉग सही ढंग से लिखे गए, अन्य प्रक्रियाओं को बताया जाएगा कि वर्तमान प्रक्रिया दुर्घटनाग्रस्त हो रही है, आदि .. संक्षेप में, यह RAII है।
23

आपने बाहर छोड़ दिया: यह प्रभावी रूप से एक try {...} catch (<specified exceptions>) { <do whatever> } catch (...) { unexpected(); ]निर्माण में सब कुछ लपेटता है , चाहे आप वहां एक कोशिश ब्लॉक चाहते हैं या नहीं।
डेविड थोरले

4
@paercebal यह गलत है। यह क्रियान्वित परिभाषित है कि क्या अनिर्दिष्ट अपवादों के लिए विध्वंसक चलाए जाते हैं या नहीं। यदि अपवाद नहीं पकड़ा गया है, तो अधिकांश वातावरण स्टैक / रन डिस्ट्रक्टर्स को खोल नहीं सकते हैं। यदि आप अपवाद को फेंकने के बावजूद अपने विध्वंसक तरीके से चलना चाहते हैं, तो यह सुनिश्चित करें कि इसे संभाला नहीं गया है (यह संदिग्ध मूल्य है), आपको अपना कोड लिखना होगा जैसेtry { <<...code...>> } catch(...) /* stack guaranteed to be unwound here and dtors run */ { throw; /* pass it on to the runtime */ }
लोगान कैपल्डो

" एप्लिकेशन को अनचाहे अपवाद के साथ क्रैश होने देना हमेशा बेहतर होता है, जिसमें से आप चुपचाप / हिंसक तरीके से मरने के बजाय एक स्टैक-ट्रेस प्राप्त कर सकते हैं। " क्लीन कॉल से बेहतर "क्रैश" कैसे हो सकता है terminate()? सिर्फ कॉल क्यों नहीं करते abort()?
जिज्ञासु

42

C ++ में अपवाद विनिर्देशों से बचें। आपके द्वारा दिए गए कारण आपके प्रश्न के लिए बहुत अच्छी शुरुआत है।

हर्ब सटर का "एक्सेप्शन स्पेसिफिकेशन्स पर एक व्यावहारिक नज़र" देखें


3
@ दाऊद: 'डायनेमिक-अपवाद-विशिष्टताओं' ( throw(optional-type-id-list)) का उपयोग C ++ 11 में किया गया है। वे अभी भी मानक में हैं, लेकिन मुझे लगता है कि एक चेतावनी शॉट भेजा गया है कि उनके उपयोग को सावधानी से माना जाना चाहिए। C ++ 11 noexceptविनिर्देश और ऑपरेटर जोड़ता है । मुझे इस noexceptपर टिप्पणी करने के बारे में पर्याप्त जानकारी नहीं है। यह लेख अधिक विस्तृत प्रतीत होता है: akrzemi1.wordpress.com/2011/06/10/use-noexcept और Dietmar Kühl का जून 2011 में एक लेख है अधिभार जर्नल: accu.org/var/uploads/jnnals/overload103.pdf
माइकल बूर

@MichaelBurr केवल throw(something)बेकार और एक बुरा विचार माना जाता है। throw()उपयोगी है।
जिज्ञासु

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

14

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

इसके अलावा मुझे लगता है कि यह अपवाद की गारंटी के लायक है।

ये मूल रूप से प्रलेखन हैं कि किसी वस्तु की स्थिति कैसे प्रभावित होती है, उस वस्तु पर एक विधि से भागने के अपवादों से प्रभावित होता है। दुर्भाग्य से वे संकलक द्वारा अन्यथा लागू या अन्यथा उल्लेखित नहीं हैं।
बूस्ट और अपवाद

अपवाद की गारंटी

कोई गारंटी नहीं:

एक विधि से बचने के बाद वस्तु की स्थिति के बारे में कोई गारंटी नहीं है
। इन स्थितियों में वस्तु का उपयोग नहीं किया जाना चाहिए।

मूल गारंटी:

लगभग सभी स्थितियों में यह एक न्यूनतम गारंटी होनी चाहिए जो एक विधि प्रदान करती है।
यह गारंटी देता है कि ऑब्जेक्ट की स्थिति अच्छी तरह से परिभाषित है और अभी भी लगातार उपयोग की जा सकती है।

मजबूत गारंटी: (उर्फ लेन-देन की गारंटी)

यह गारंटी देता है कि विधि सफलतापूर्वक पूरी हो जाएगी
या एक अपवाद फेंक दिया जाएगा और ऑब्जेक्ट स्थिति नहीं बदलेगी।

कोई गारंटी नहीं:

विधि गारंटी देती है कि किसी भी अपवाद को विधि से बाहर प्रचारित करने की अनुमति नहीं है।
सभी विध्वंसक को यह गारंटी देनी चाहिए।
| NB यदि कोई अपवाद पहले से ही प्रचारित हो तो एक विध्वंसक बच जाता है
| आवेदन समाप्त हो जाएगा


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

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

" यहां बताया गया है कि कई लोग सोचते हैं कि अपवाद विनिर्देश क्या करते हैं: - गारंटी दें कि फ़ंक्शन केवल सूचीबद्ध अपवादों (संभवतः कोई भी नहीं) को फेंक देंगे। - ज्ञान के आधार पर संकलक अनुकूलन सक्षम करें जो केवल सूचीबद्ध अपवादों (संभवत: कोई नहीं) को फेंक दिया जाएगा। उपरोक्त अपेक्षाएं हैं। फिर से, भ्रामक रूप से सही होने के करीब। "वास्तव में, दोनों बिल्कुल सही हैं।
२५'१

@curiousguy। यह जावा ऐसा कैसे करता है क्योंकि यह संकलन समय पर लागू होता है। C ++ रनटाइम चेक हैं। तो: Guarantee that functions will only throw listed exceptions (possibly none)। सच नहीं। यह केवल गारंटी देता है कि यदि फ़ंक्शन इन अपवादों को फेंक देता है तो आवेदन समाप्त हो जाएगा। Enable compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrownऐसा नहीं कर सकते। क्योंकि मैंने अभी बताया कि आप यह गारंटी नहीं दे सकते कि अपवाद नहीं फेंका जाएगा।
मार्टिन जॉर्ज

1
@LokiAstari "यह जावा कैसे करता है क्योंकि यह जाँच संकलन समय पर लागू किया जाता है_" जावा अपवाद युक्ति एक प्रयोग है जो विफल हो गया है। जावा में सबसे उपयोगी अपवाद कल्पना नहीं है: throw()(फेंक नहीं देता है)। " यह केवल गारंटी देता है कि यदि फ़ंक्शन इन अपवादों को फेंक देता है तो आवेदन " नहीं "सच नहीं होगा" का अर्थ है। C ++ में कोई गारंटी नहीं है कि कोई फ़ंक्शन कॉल नहीं करता है terminate(), कभी भी। " क्योंकि मैंने अभी बताया कि आप यह गारंटी नहीं दे सकते कि अपवाद नहीं फेंका जाएगा " आप गारंटी देते हैं कि फ़ंक्शन फेंक नहीं होगा। जो वास्तव में आपको चाहिए।
जिज्ञासु

8

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


7

एकमात्र उपयोगी अपवाद स्पेसियर "फेंक ()" है, जैसा कि "फेंक नहीं" है।


2
क्या आप कृपया एक कारण जोड़ सकते हैं कि यह क्यों उपयोगी है?
बूटी-आका

2
यह उपयोगी क्यों नहीं है? कुछ फ़ंक्शन को जानने से ज्यादा उपयोगी कुछ नहीं है, सही और केंद्र छोड़ दिए गए अपवादों को फेंकना शुरू नहीं करना है।
मैट जॉइनर

1
कृपया विस्तृत विवरण के लिए माइकल ब्यूर के उत्तर में संदर्भित हर्ब सटर चर्चा देखें।
हेरोल्ड एक्स्ट्रॉम

4

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

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

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


3

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

अन्यथा मुझे कुछ भी उपयोग करने के लिए विशेष रूप से उपयोगी नहीं लगता है लेकिन throw()यह इंगित करने के लिए कि यह कोई अपवाद नहीं फेंकता है।


3

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

इसके अलावा, मेरा मानना ​​है कि उनका उपयोग C ++ 0x मानक के वर्तमान ड्राफ्ट में किया गया है।


3

एक "थ्रो ()" विनिर्देश कंपाइलर को कोड प्रवाह विश्लेषण करते समय कुछ अनुकूलन करने की अनुमति देता है यदि यह जानता है कि फ़ंक्शन कभी भी अपवाद नहीं छोड़ेगा (या कम से कम कभी अपवाद नहीं फेंकने का वादा करता है)। लैरी ओस्टरमैन इस बारे में संक्षेप में यहां बात करते हैं:

http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx


2

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

हां, अपवाद निर्दिष्टकर्ताओं के साथ एक फ़ंक्शन से फेंके जा रहे गैर-निर्दिष्ट अपवाद का अपेक्षित व्यवहार समाप्ति () को कॉल करना है।

मैं यह भी नोट करूंगा कि स्कॉट मेयर्स इस विषय को और अधिक प्रभावी C ++ में संबोधित करते हैं। उनकी प्रभावी C ++ और अधिक प्रभावी C ++ अत्यधिक अनुशंसित पुस्तकें हैं।


2

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

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


2

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


0

लेख से:

http://www.boost.org/community/exception_safety.html

"एक अपवाद-सुरक्षित जेनेरिक कंटेनर को लिखना असंभव माना जाता है।" यह दावा अक्सर टॉम कारगिल [4] के एक लेख के संदर्भ में सुना जाता है, जिसमें वह एक सामान्य स्टैक टेम्पलेट के लिए अपवाद-सुरक्षा की समस्या की पड़ताल करते हैं। अपने लेख में, कारगिल कई उपयोगी प्रश्न उठाता है, लेकिन दुर्भाग्य से अपनी समस्या का समाधान प्रस्तुत करने में विफल रहता है ।1 उन्होंने यह सुझाव देकर निष्कर्ष निकाला है कि एक समाधान संभव नहीं हो सकता है। दुर्भाग्य से, उनके लेख को उस अटकल के "प्रमाण" के रूप में कई लोगों द्वारा पढ़ा गया था। चूंकि यह प्रकाशित किया गया था, उनमें सी + + मानक पुस्तकालय कंटेनरों में अपवाद-सुरक्षित जेनेरिक घटकों के कई उदाहरण हैं।

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


-2

अपवाद विनिर्देशों = बकवास, 30 वर्ष से अधिक उम्र के किसी भी जावा डेवलपर से पूछें


8
30 वर्ष से अधिक आयु के जावा प्रोग्रामर को बुरा महसूस होना चाहिए कि वे सी के साथ सामना नहीं कर सकते हैं, उनके पास जावा का उपयोग करने का कोई बहाना नहीं है।
मैट जॉइनर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.