फ़ंक्शन के हस्ताक्षर में कीवर्ड फेंको


200

throwफ़ंक्शन हस्ताक्षर में C ++ कीवर्ड का उपयोग करने के लिए बुरा व्यवहार क्यों माना जाता है इसका तकनीकी कारण क्या है ?

bool some_func() throw(myExc)
{
  ...
  if (problem_occurred) 
  {
    throw myExc("problem occurred");
  }
  ...
}

हाल ही के इस संबंधित प्रश्न को देखें: stackoverflow.com/questions/1037575/…
laalto


1
noexceptकुछ बदलता है क्या ?
आरोन मैकडैड

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

यह ध्यान दिया जाना चाहिए कि सी ++ 11 के बाद से अपवाद विनिर्देशों को हटा दिया गया है।
फ्राँस्वा एंड्रीक्स

जवाबों:


127

नहीं, यह अच्छा अभ्यास नहीं माना जाता है। इसके विपरीत, यह आमतौर पर एक बुरा विचार माना जाता है।

http://www.gotw.ca/publications/mill22.htm क्यों के बारे में बहुत अधिक विस्तार में चला जाता है, लेकिन समस्या आंशिक रूप से है कि कंपाइलर इसे लागू करने में असमर्थ है, इसलिए इसे रनटाइम पर जांचना होगा, जो आमतौर पर है अवांछनीय। और यह किसी भी मामले में अच्छी तरह से समर्थित नहीं है। (MSVC थ्रो को छोड़कर, अपवाद विनिर्देशों की उपेक्षा करता है, जिसे वह इस गारंटी के रूप में व्याख्या करता है कि कोई अपवाद नहीं फेंका जाएगा।


25
हाँ। थ्रो (myEx) की तुलना में आपके कोड में व्हॉट्सएप जोड़ने के बेहतर तरीके हैं।
असफ लवी

4
हाँ, जो लोग केवल अपवाद विशिष्टताओं की खोज करते हैं वे अक्सर यह मानते हैं कि वे जावा की तरह काम करते हैं, जहां कंपाइलर उन्हें लागू करने में सक्षम है। C ++ में, ऐसा नहीं होगा, जो उन्हें बहुत कम उपयोगी बनाता है।
जुलफ

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

1
@ एंडर्स लिंडन दस्तावेजी उद्देश्य क्या है? यदि आप अपने कोड के व्यवहार का दस्तावेजीकरण करना चाहते हैं, तो बस इसके ऊपर एक टिप्पणी डालें। निश्चित नहीं कि दूसरे भाग के साथ आपका क्या मतलब है। यदि आप कोशिश भी करते हैं तो अपवाद कभी नहीं पकड़ पाएंगे?
१०:१६

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

57

जलफ पहले से ही इससे जुड़ा हुआ है, लेकिन GOTW इसे काफी अच्छी तरह से बताता है कि अपवाद विनिर्देश उतने उपयोगी नहीं हैं जितना कोई उम्मीद कर सकता है:

int Gunc() throw();    // will throw nothing (?)
int Hunc() throw(A,B); // can only throw A or B (?)

क्या टिप्पणियाँ सही हैं? काफी नहीं। Gunc()वास्तव में कुछ फेंक सकते हैं, और Hunc()अच्छी तरह से ए या बी के अलावा कुछ फेंक सकते हैं! कंपाइलर केवल उन्हें बेहोश करने की गारंटी देता है यदि वे करते हैं ... ओह, और अपने कार्यक्रम को भी बहुत समय के लिए बेहोश करने के लिए।

बस यही है कि यह नीचे आता है, आप शायद एक कॉल के साथ समाप्त हो जाएगा terminate()और आपका कार्यक्रम एक त्वरित लेकिन दर्दनाक मौत मर जाएगा।

GOTWs निष्कर्ष है:

तो यहाँ पर आज के रूप में एक समुदाय के रूप में हमने जो सबसे अच्छी सलाह दी है वह सबसे अच्छी लगती है:

  • Moral # 1: कभी भी अपवाद विनिर्देश न लिखें।
  • Moral # 2: संभवतः एक खाली को छोड़कर, लेकिन अगर मैं होता तो मैं भी उससे बचता।

1
मुझे यकीन नहीं है कि मैं एक अपवाद क्यों फेंकूंगा और इसका उल्लेख नहीं कर पाऊंगा। यहां तक ​​कि अगर यह किसी अन्य फ़ंक्शन द्वारा फेंका जाता है, तो मुझे पता है कि अपवाद क्या फेंका जा सकता है। एकमात्र कारण जो मैं देख सकता हूं, क्योंकि यह थकाऊ है।
MasterMastic

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

1
ओह ठीक है, जवाब देने के लिए धन्यवाद। मुझे लगता है कि प्रलेखन क्या है।
मास्टरमैस्ट

2
गलत। अपवाद विनिर्देश लिखा जाना चाहिए, लेकिन विचार यह है कि कॉल करने वाले को पकड़ने के लिए किन त्रुटियों को पकड़ने की कोशिश करनी चाहिए।
हैलोवर्ल्ड

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

30

इस प्रश्न के अन्य सभी उत्तर के लिए थोड़ा अधिक मूल्य जोड़ने के लिए, किसी को प्रश्न में कुछ मिनट का निवेश करना चाहिए: निम्नलिखित कोड का आउटपुट क्या है?

#include <iostream>
void throw_exception() throw(const char *)
{
    throw 10;
}
void my_unexpected(){
    std::cout << "well - this was unexpected" << std::endl;
}
int main(int argc, char **argv){
    std::set_unexpected(my_unexpected);
    try{
        throw_exception();
    }catch(int x){
        std::cout << "catch int: " << x << std::endl;
    }catch(...){
        std::cout << "catch ..." << std::endl;
    }
}

उत्तर: जैसा कि यहां बताया गया है , कार्यक्रम कॉल करता है std::terminate()और इस प्रकार अपवाद संचालकों में से कोई भी फोन नहीं करेगा।

विवरण: पहला my_unexpected()फ़ंक्शन कहा जाता है, लेकिन चूंकि यह throw_exception()फ़ंक्शन प्रोटोटाइप के लिए मिलान अपवाद प्रकार को फिर से फेंक नहीं देता है, अंत में, std::terminate()इसे कहा जाता है। तो पूर्ण उत्पादन इस तरह दिखता है:

user @ उपयोगकर्ता: ~ / tmp $ g ++ -o छोड़कर.test.test.cpp
उपयोगकर्ता @ उपयोगकर्ता: ~ / tmp $ ./except.test
अच्छी तरह से - यह
'int' का एक उदाहरण फेंकने के बाद अनपेक्षित समाप्त किया गया था
(कोर) फेंक दिया)


12

थ्रो स्पेसियर का एकमात्र व्यावहारिक प्रभाव यह है कि यदि myExcआपके फ़ंक्शन द्वारा फेंकी गई चीज़ से कुछ अलग std::unexpectedकिया जाता है , तो उसे (सामान्य अखंडित अपवाद तंत्र के बजाय) कहा जाएगा।

इस प्रकार के अपवादों को दस्तावेज़ित करने के लिए जो एक फ़ंक्शन को फेंक सकते हैं, मैं आमतौर पर ऐसा करता हूं:

bool
some_func() /* throw (myExc) */ {
}

5
यह भी नोट करना उपयोगी है कि std :: अनपेक्षित () आमतौर पर std :: terminate () और आपके प्रोग्राम के अचानक समाप्त होने के परिणामस्वरूप कॉल होता है।
sth

1
- और वह MSVC कम से कम इस व्यवहार को लागू नहीं करता है जहां तक ​​मुझे पता है।
जुलफ २al

9

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

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


9

खैर, इस फेंक विनिर्देश के बारे में गुगली करते हुए, इस लेख पर मेरी नज़र थी: - ( http://blogs.msdn.com/b/larryosterman/archive/2006/03/22/558390.aspx )

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

   class MyClass
   {
    size_t CalculateFoo()
    {
        :
        :
    };
    size_t MethodThatCannotThrow() throw()
    {
        return 100;
    };
    void ExampleMethod()
    {
        size_t foo, bar;
        try
        {
            foo = CalculateFoo();
            bar = foo * 100;
            MethodThatCannotThrow();
            printf("bar is %d", bar);
        }
        catch (...)
        {
        }
    }
};

जब कंपाइलर इसे "थ्रो ()" विशेषता के साथ देखता है, तो कंपाइलर "बार" वेरिएबल को पूरी तरह से ऑप्टिमाइज़ कर सकता है, क्योंकि यह जानता है कि मेथडॉटनॉटनथ्रो () से फेंके जाने वाले अपवाद के लिए कोई रास्ता नहीं है। थ्रो () विशेषता के बिना, कंपाइलर को "बार" वैरिएबल बनाना पड़ता है, क्योंकि यदि मेथडॉटनॉटनोट्रो एक अपवाद फेंकता है, तो अपवाद हैंडलर बार वैरिएबल के मान पर निर्भर हो सकता है / होगा।

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


3

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

कुछ कंपाइलर्स के साथ नॉन-इनलाइन फ़ंक्शंस पर नो-थ्रो विनिर्देशन फायदेमंद हो सकता है यदि सही अनुकूलन किया जाता है और उस फ़ंक्शन का उपयोग इस तरह से प्रदर्शन को प्रभावित करता है कि वह इसे सही ठहराता है।

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

जल्दी में उन लोगों के लिए उपरोक्त लिंक का एक उद्धरण ( एक भोले संकलक से एक इनलाइन समारोह पर फेंक फेंक को निर्दिष्ट करने के बुरे अनपेक्षित प्रभावों का एक उदाहरण है ):

अपवाद-विनिर्देश तर्क

अपवाद विनिर्देशों [आईएसओ 15.4] को कभी-कभी यह इंगित करने के लिए कोडित किया जाता है कि अपवाद क्या फेंका जा सकता है, या क्योंकि प्रोग्रामर को उम्मीद है कि वे प्रदर्शन में सुधार करेंगे। लेकिन एक स्मार्ट पॉइंटर से निम्नलिखित सदस्य पर विचार करें:

टी एंड ऑपरेटर * () कास्ट थ्रो () {रिटर्न * पीटीआर; }

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

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

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

एक गैर-इनलाइन फ़ंक्शन एक ऐसी जगह है जो "कुछ भी नहीं फेंकता है" अपवाद-विनिर्देश कुछ संकलक के साथ कुछ लाभ हो सकता है।


1
"अपवाद-विशिष्टताओं के साथ सबसे बड़ी समस्या यह है कि प्रोग्रामर उनका उपयोग करते हैं जैसे कि वे प्रोग्रामर को प्रभावित करते हैं, वे वास्तव में उनके प्रभाव के बजाय चाहेंगे।" मशीनों या लोगों के साथ संवाद करते समय त्रुटियों के लिए यह # 1 कारण है: हम जो कहते हैं और जो हमारा मतलब है, उसके बीच का अंतर।
थगोमाइज़र
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.