C ++ अपवाद कैसे फेंकें


259

मुझे अपवाद हैंडलिंग की बहुत खराब समझ है (यानी, थ्रो को कैसे कस्टमाइज़ किया जाए, कोशिश करें, अपने उद्देश्यों के लिए स्टेटमेंट पकड़ें)।

उदाहरण के लिए, मैंने एक फ़ंक्शन को निम्नानुसार परिभाषित किया है: int compare(int a, int b){...}

मैं फ़ंक्शन को कुछ संदेश के साथ अपवाद फेंकने के लिए पसंद करता हूं जब या तो बी या नकारात्मक होता है।

मुझे फ़ंक्शन की परिभाषा में यह कैसे दृष्टिकोण करना चाहिए?


3
आप इस पढ़ना चाहिए: gotw.ca/publications/mill22.htm
ओलिवर चार्ल्सवर्थ

37
@OliCharlesworth, क्या आपको नहीं लगता कि मूल बातें से भ्रमित किसी व्यक्ति पर फेंकना थोड़ा अधिक है?
मार्क रैनसम

6
सतही अपवादों से बचने के लायक हैं। यदि आप नहीं चाहते हैं कि आपका कॉलर नकारात्मक मानों को पारित करे, तो यह unsigned intआपके फ़ंक्शन हस्ताक्षर में पैरामीटर के रूप में निर्दिष्ट करके इसे और अधिक स्पष्ट करता है। फिर से मैं स्कूल का हूं कि आपको केवल उन चीजों के अपवादों को फेंकना और पकड़ना चाहिए जो वास्तव में असाधारण हैं।
AJG85

1
@ मर्क: मैं मूल रूप से इस सवाल को गलत मानता हूं कि क्या किसी को throw()फ़ंक्शंस पर अपवाद विनिर्देशों का उपयोग करना चाहिए ।
ओलिवर चार्ल्सवर्थ

जवाबों:


363

सरल:

#include <stdexcept>

int compare( int a, int b ) {
    if ( a < 0 || b < 0 ) {
        throw std::invalid_argument( "received negative value" );
    }
}

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

try {
    compare( -1, 3 );
}
catch( const std::invalid_argument& e ) {
    // do stuff with exception... 
}

प्रत्येक प्रयास के बाद आपके पास कई कैच () स्टेटमेंट हो सकते हैं, इसलिए यदि आप चाहें तो अलग-अलग अपवाद प्रकारों को अलग-अलग संभाल सकते हैं।

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

catch( const std::invalid_argument& e ) {
    // do something

    // let someone higher up the call stack handle it if they want
    throw;
}

और प्रकार की परवाह किए बिना अपवादों को पकड़ने के लिए:

catch( ... ) { };

26
और आपको हमेशा कॉन्स्ट के रूप में अपवादों को पकड़ना चाहिए
एड्रियन कोर्निश

2
@TerryLiYifeng यदि कस्टम अपवाद अधिक समझ में आते हैं तो इसके लिए जाएं। आप अभी भी std :: अपवाद से प्राप्त कर सकते हैं और इंटरफ़ेस को समान रख सकते हैं।
nsanders

2
+ 1'द फिर से लेकिन मुझे लगता है कि इसका बहुत महत्वपूर्ण हिस्सा है - क्योंकि यह इस तथ्य को उजागर करता है कि यह अब एक अस्थायी वस्तु है - इसलिए संशोधन बेकार है।
एड्रियन कोर्निश

2
@ एड्रियनकोर्निश: यह वास्तव में अस्थायी नहीं है। गैर-कास्ट कैच उपयोगी हो सकता है
GManNickG

26
आप आम तौर पर एक साधारण throw;(मूल वस्तु को फिर से उखाड़ फेंकते हैं और इसके प्रकार को संरक्षित करते हैं) के बजाय उखाड़ throw e;फेंकते हैं (पकड़े हुए ऑब्जेक्ट की एक प्रति फेंकते हुए, संभवतः इसके प्रकार को बदलते हुए)।
माइक सीमोर

17

throwजहां जरूरत हो, वहीं जोड़ें और tryत्रुटि को संभालने वाले कॉलर को ब्लॉक करें। अधिवेशन द्वारा आपको केवल उन चीजों को फेंकना चाहिए जो इससे प्राप्त होती हैं std::exception, इसलिए <stdexcept>पहले शामिल करें ।

int compare(int a, int b) {
    if (a < 0 || b < 0) {
        throw std::invalid_argument("a or b negative");
    }
}

void foo() {
    try {
        compare(-1, 0);
    } catch (const std::invalid_argument& e) {
        // ...
    }
}

इसके अलावा, Boost.Exception में देखें


15

हालांकि यह सवाल पुराना है और पहले से ही उत्तर दे दिया गया है, मैं सिर्फ 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"

8

जब कोई निश्चित त्रुटि होती है, तो आप एक संदेश को फेंक सकते हैं:

throw std::invalid_argument( "received negative value" );

या आप इसे इस तरह परिभाषित कर सकते हैं:

std::runtime_error greatScott("Great Scott!");          
double getEnergySync(int year) {                        
    if (year == 1955 || year == 1885) throw greatScott; 
    return 1.21e9;                                      
}                                                       

आमतौर पर, आपके पास try ... catchइस तरह का एक ब्लॉक होगा:

try {
// do something that causes an exception
}catch (std::exception& e){ std::cerr << "exception: " << e.what() << std::endl; }

6

कस्टम अपवादों के मामले में अतिरिक्त विवरण के लिए यहां बताए गए अन्य उत्तरों के लिए ADD से संबंधित है

उस मामले में जहां आप अपने स्वयं के कस्टम अपवाद बनाते हैं, जो व्युत्पन्न होता है std::exception, जब आप "सभी संभव" अपवाद प्रकारों को पकड़ते हैं, तो आपको हमेशा catch"सबसे व्युत्पन्न" अपवाद प्रकार के साथ क्लॉस शुरू करना चाहिए जो पकड़ा जा सकता है। उदाहरण देखें (क्या नहीं करना है):

#include <iostream>
#include <string>

using namespace std;

class MyException : public exception
{
public:
    MyException(const string& msg) : m_msg(msg)
    {
        cout << "MyException::MyException - set m_msg to:" << m_msg << endl;
    }

   ~MyException()
   {
        cout << "MyException::~MyException" << endl;
   }

   virtual const char* what() const throw () 
   {
        cout << "MyException - what" << endl;
        return m_msg.c_str();
   }

   const string m_msg;
};

void throwDerivedException()
{
    cout << "throwDerivedException - thrown a derived exception" << endl;
    string execptionMessage("MyException thrown");
    throw (MyException(execptionMessage));
}

void illustrateDerivedExceptionCatch()
{
    cout << "illustrateDerivedExceptionsCatch - start" << endl;
    try 
    {
        throwDerivedException();
    }
    catch (const exception& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an std::exception, e.what:" << e.what() << endl;
        // some additional code due to the fact that std::exception was thrown...
    }
    catch(const MyException& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an MyException, e.what::" << e.what() << endl;
        // some additional code due to the fact that MyException was thrown...
    }

    cout << "illustrateDerivedExceptionsCatch - end" << endl;
}

int main(int argc, char** argv)
{
    cout << "main - start" << endl;
    illustrateDerivedExceptionCatch();
    cout << "main - end" << endl;
    return 0;
}

ध्यान दें:

०) उचित क्रम इसके विपरीत होना चाहिए, अर्थात- पहले आप catch (const MyException& e)जो इसके बाद हैं catch (const std::exception& e)

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

2) भले ही पहला कैच क्लॉज में पकड़ा गया प्रकार हो std::exception, लेकिन "उचित" संस्करण को what()कहा जाएगा - क्योंकि यह संदर्भ द्वारा पकड़ा गया है (कम से कम पकड़े गए तर्क std::exceptionप्रकार को मूल्य के आधार पर बदलें - और आप अनुभव करेंगे "ऑब्जेक्ट स्लाइसिंग" क्रिया में घटना)।

3) इस मामले में कि "XXX कोड को फेंके जाने के तथ्य के कारण कुछ कोड ..." अपवाद के प्रकार के साथ महत्वपूर्ण सामान करता है, यहां आपके कोड का दुर्व्यवहार है।

4) यह भी प्रासंगिक है यदि पकड़े गए ऑब्जेक्ट "सामान्य" वस्तु जैसे: class Base{};और class Derived : public Base {}...

5) g++ 7.3.0उबंटू 18.04.1 पर एक चेतावनी है जो उल्लिखित मुद्दे को इंगित करती है:

फ़ंक्शन में 'शून्य चित्रणड्रेडएक्सडैप्शनकैच ()': item12Linux.cpp: 48: 2: चेतावनी: 'MyException' प्रकार के अपवाद को पकड़ा जाएगा (const MyException & e) ^ ~~~~

item12Linux.cpp: 43: 2: चेतावनी: 'std :: अपवाद' के लिए पहले हैंडलर द्वारा (कास्ट अपवाद और e) ^ ~~~~

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

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