खोज…


अपवादों को पकड़ना

अपवादों को पकड़ने के लिए एक try/catch ब्लॉक का उपयोग किया जाता है। try अनुभाग में कोड वह कोड है जो अपवाद को फेंक सकता है, और catch क्लॉज़ (एस) में कोड अपवाद को संभालता है।

#include <iostream>
#include <string>
#include <stdexcept>

int main() {
  std::string str("foo");
  
  try {
      str.at(10); // access element, may throw std::out_of_range
  } catch (const std::out_of_range& e) {
      // what() is inherited from std::exception and contains an explanatory message
      std::cout << e.what();
  }
}

एकाधिक अपवाद प्रकारों को संभालने के लिए एकाधिक catch क्लॉज़ का उपयोग किया जा सकता है। यदि कई catch क्लॉज़ मौजूद हैं, तो कोड में उनकी उपस्थिति के क्रम में अपवाद हैंडलिंग तंत्र उन्हें मैच करने की कोशिश करता है:

std::string str("foo");
  
try {
    str.reserve(2); // reserve extra capacity, may throw std::length_error
    str.at(10); // access element, may throw std::out_of_range
} catch (const std::length_error& e) {
    std::cout << e.what();
} catch (const std::out_of_range& e) {
    std::cout << e.what();
}

अपवाद वर्ग जो एक सामान्य आधार वर्ग से प्राप्त होते हैं, उन्हें सामान्य आधार वर्ग के लिए एकल catch क्लॉज के साथ पकड़ा जा सकता है। उपरोक्त उदाहरण std::length_error और std::out_of_range के लिए एक क्लॉज के साथ दो catch क्लाज को प्रतिस्थापित कर सकता है std:exception :

std::string str("foo");
  
try {
    str.reserve(2); // reserve extra capacity, may throw std::length_error
    str.at(10); // access element, may throw std::out_of_range
} catch (const std::exception& e) {
    std::cout << e.what();
}

चूँकि catch क्लॉज़ को क्रम में रखने की कोशिश की जाती है, इसलिए पहले से अधिक विशिष्ट कैच क्लॉज़ लिखना सुनिश्चित करें, अन्यथा आपके अपवाद हैंडलिंग कोड को शायद मिल सकता है:

try {
    /* Code throwing exceptions omitted. */
} catch (const std::exception& e) {
    /* Handle all exceptions of type std::exception. */
} catch (const std::runtime_error& e) {
    /* This block of code will never execute, because std::runtime_error inherits
       from std::exception, and all exceptions of type std::exception were already
       caught by the previous catch clause. */
}

एक और संभावना कैच-ऑल हैंडलर है, जो किसी भी फेंकी हुई वस्तु को पकड़ लेगा:

try {
    throw 10;
} catch (...) {
    std::cout << "caught an exception";
}

रेथ्रो (प्रचार) अपवाद

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

try {
    ... // some code here
} catch (const SomeException& e) {
    std::cout << "caught an exception";
    throw;
}

throw; का उपयोग करना throw; तर्कों के बिना वर्तमान में पकड़े गए अपवाद को फिर से फेंक देंगे।

सी ++ 11

किसी प्रबंधित std::exception_ptr rethrow_exception को rethrow_exception लिए, C ++ मानक लाइब्रेरी में rethrow_exception फ़ंक्शन है जिसका उपयोग आपके प्रोग्राम में <exception> शीर्षलेख को शामिल करके किया जा सकता है।

#include <iostream>
#include <string>
#include <exception>
#include <stdexcept>
 
void handle_eptr(std::exception_ptr eptr) // passing by value is ok
{
    try {
        if (eptr) {
            std::rethrow_exception(eptr);
        }
    } catch(const std::exception& e) {
        std::cout << "Caught exception \"" << e.what() << "\"\n";
    }
}
 
int main()
{
    std::exception_ptr eptr;
    try {
        std::string().at(1); // this generates an std::out_of_range
    } catch(...) {
        eptr = std::current_exception(); // capture
    }
    handle_eptr(eptr);
} // destructor for std::out_of_range called here, when the eptr is destructed

रचनाकार में ब्लॉक की कोशिश करें

शुरुआती सूची में अपवाद को पकड़ने का एकमात्र तरीका:

struct A : public B
{
    A() try : B(), foo(1), bar(2)
    {
        // constructor body 
    }
    catch (...)
    {
        // exceptions from the initializer list and constructor are caught here
        // if no exception is thrown here
        // then the caught exception is re-thrown.
    }
 
private:
    Foo foo;
    Bar bar;
};

नियमित कार्य के लिए ब्लॉक का प्रयास करें

void function_with_try_block() 
try
{
    // try block body
} 
catch (...) 
{ 
    // catch block body
}

के बराबर है

void function_with_try_block() 
{
    try
    {
        // try block body
    } 
    catch (...) 
    { 
        // catch block body
    }
}

ध्यान दें कि कंस्ट्रक्टर्स और डिस्ट्रक्टर्स के लिए, व्यवहार अलग-अलग होता है क्योंकि कैच ब्लॉक री-थ्रो फिर भी एक अपवाद है (कैच ब्लॉक बॉडी में कोई अन्य थ्रो नहीं होने पर पकड़ा गया)।

फ़ंक्शन main को किसी अन्य फ़ंक्शन की तरह एक फ़ंक्शन ट्रायल ब्लॉक करने की अनुमति है, लेकिन main फ़ंक्शन फ़ंक्शन ब्लॉक एक गैर-स्थानीय स्थैतिक चर के निर्माण या किसी भी स्थिर चर के विनाश के दौरान होने वाले अपवादों को नहीं पकड़ेगा। इसके बजाय, std::terminate को कहा जाता है।

विनाश में फंक्शन की कोशिश करें

struct A
{
    ~A() noexcept(false) try
    {
        // destructor body 
    }
    catch (...)
    {
        // exceptions of destructor body are caught here
        // if no exception is thrown here
        // then the caught exception is re-thrown.
    }
};

ध्यान दें, हालांकि यह संभव है, किसी को विध्वंसक से फेंकने के साथ बहुत सावधानी बरतने की आवश्यकता होती है, जैसे कि स्टैक अनइंडिंग के दौरान कहा जाने वाला एक विध्वंसक एक अपवाद फेंकता है, std::terminate को कहा जाता है।

सबसे अच्छा अभ्यास: मूल्य से फेंक, कब्ज संदर्भ द्वारा पकड़

सामान्य तौर पर, इसे मूल्य (सूचक द्वारा) के बजाय फेंकने के लिए अच्छा अभ्यास माना जाता है, लेकिन (कॉन्स्ट) संदर्भ द्वारा पकड़ते हैं।

try {
    // throw new std::runtime_error("Error!");   // Don't do this!
    // This creates an exception object
    // on the heap and would require you to catch the
    // pointer and manage the memory yourself. This can
    // cause memory leaks!
    
    throw std::runtime_error("Error!");
} catch (const std::runtime_error& e) {
    std::cout << e.what() << std::endl;
}

संदर्भ द्वारा पकड़ने का एक कारण यह है कि यह एक अच्छा अभ्यास है कि यह कैच ब्लॉक (या जब अन्य कैच ब्लॉक्स के माध्यम से प्रचारित हो रहा हो) को पास होने पर ऑब्जेक्ट को फिर से संगठित करने की आवश्यकता को समाप्त कर देता है। संदर्भ द्वारा पकड़ने से भी अपवाद पॉलीमॉर्फिक रूप से नियंत्रित किया जा सकता है और ऑब्जेक्ट स्लाइसिंग से बचा जाता है। हालाँकि, यदि आप एक अपवाद को हटा रहे हैं (जैसे throw e; , नीचे उदाहरण देखें), तो आप अभी भी ऑब्जेक्ट स्लिंग प्राप्त कर सकते हैं क्योंकि throw e; स्टेटमेंट अपवाद की एक प्रति बनाता है जो कुछ भी घोषित किया जाता है:

#include <iostream>

struct BaseException {
    virtual const char* what() const { return "BaseException"; }
};

struct DerivedException : BaseException {
    // "virtual" keyword is optional here
    virtual const char* what() const { return "DerivedException"; }
};

int main(int argc, char** argv) {
    try {
        try {
            throw DerivedException();
        } catch (const BaseException& e) {
            std::cout << "First catch block: " << e.what() << std::endl;
            // Output ==> First catch block: DerivedException

            throw e; // This changes the exception to BaseException
                     // instead of the original DerivedException!
        }
    } catch (const BaseException& e) {
        std::cout << "Second catch block: " << e.what() << std::endl;
        // Output ==> Second catch block: BaseException
    }
    return 0;
}

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

चेतावनी: catch ब्लॉकों में अनपेक्षित अपवादों को फेंकने से सावधान रहें, विशेष रूप से अतिरिक्त मेमोरी या संसाधनों को आवंटित करने से संबंधित। उदाहरण के लिए, निर्माण logic_error , runtime_error या उनके उपवर्गों फेंक सकती है bad_alloc निकलता जा रहा है जब अपवाद स्ट्रिंग को कॉपी स्मृति के कारण, आई / ओ धाराओं संबंधित अपवाद मास्क सेट, आदि के साथ प्रवेश के दौरान फेंक सकती है

नेस्टेड अपवाद

सी ++ 11

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

नेस्टेड अपवाद का उद्देश्य निम्न स्तर के अपवाद को संलग्न करके समस्या को हल करना है, जो कारण का वर्णन करता है, एक उच्च स्तर के अपवाद पर, जो यह बताता है कि इस विशेष मामले में इसका क्या मतलब है।

std::nested_exception घोंसले के अपवादों को धन्यवाद देने की अनुमति देता है std::throw_with_nested :

#include <stdexcept>
#include <exception>
#include <string>
#include <fstream>
#include <iostream>

struct MyException
{
    MyException(const std::string& message) : message(message) {}
    std::string message;
};

void print_current_exception(int level)
{
    try {
        throw;
    } catch (const std::exception& e) {
        std::cerr << std::string(level, ' ') << "exception: " << e.what() << '\n';
    } catch (const MyException& e) {
        std::cerr << std::string(level, ' ') << "MyException: " << e.message << '\n';
    } catch (...) {
        std::cerr << "Unkown exception\n";
    }
}

void print_current_exception_with_nested(int level =  0)
{
    try {
        throw;
    } catch (...) {
        print_current_exception(level);
    }    
    try {
        throw;
    } catch (const std::nested_exception& nested) {
        try {
            nested.rethrow_nested();
        } catch (...) {
            print_current_exception_with_nested(level + 1); // recursion
        }
    } catch (...) {
        //Empty // End recursion
    }
}

// sample function that catches an exception and wraps it in a nested exception
void open_file(const std::string& s)
{
    try {
        std::ifstream file(s);
        file.exceptions(std::ios_base::failbit);
    } catch(...) {
        std::throw_with_nested(MyException{"Couldn't open " + s});
    }
}
 
// sample function that catches an exception and wraps it in a nested exception
void run()
{
    try {
        open_file("nonexistent.file");
    } catch(...) {
        std::throw_with_nested( std::runtime_error("run() failed") );
    }
}
 
// runs the sample function above and prints the caught exception
int main()
{
    try {
        run();
    } catch(...) {
        print_current_exception_with_nested();
    }
}

संभावित उत्पादन:

exception: run() failed
 MyException: Couldn't open nonexistent.file
  exception: basic_ios::clear

यदि आप केवल std::exception से विरासत में मिले अपवादों के साथ काम करते std::exception , तो कोड को सरल भी बनाया जा सकता है।

std :: uncaught_exceptions

c ++ 17

C ++ 17 int std::uncaught_exceptions() परिचय देता है int std::uncaught_exceptions() (सीमित bool std::uncaught_exception() को बदलने के लिए bool std::uncaught_exception() ) यह जानने के लिए कि वर्तमान में कितने अपवाद हैं। एक वर्ग के लिए यह निर्धारित करने की अनुमति देता है कि क्या यह स्टैक अनइंडिंग के दौरान नष्ट हो गया है या नहीं।

#include <exception>
#include <string>
#include <iostream>

// Apply change on destruction:
// Rollback in case of exception (failure)
// Else Commit (success)
class Transaction
{
public:
    Transaction(const std::string& s) : message(s) {}
    Transaction(const Transaction&) = delete;
    Transaction& operator =(const Transaction&) = delete;
    void Commit() { std::cout << message << ": Commit\n"; }
    void RollBack() noexcept(true) { std::cout << message << ": Rollback\n"; }

    // ...

    ~Transaction() {
        if (uncaughtExceptionCount == std::uncaught_exceptions()) {
            Commit(); // May throw.
        } else { // current stack unwinding
            RollBack();
        }
    }

private:
    std::string message;
    int uncaughtExceptionCount = std::uncaught_exceptions();
};

class Foo
{
public:
    ~Foo() {
        try {
            Transaction transaction("In ~Foo"); // Commit,
                                            // even if there is an uncaught exception
            //...
        } catch (const std::exception& e) {
            std::cerr << "exception/~Foo:" << e.what() << std::endl;
        }
    }
};

int main()
{
    try {
        Transaction transaction("In main"); // RollBack
        Foo foo; // ~Foo commit its transaction.
        //...
        throw std::runtime_error("Error");
    } catch (const std::exception& e) {
        std::cerr << "exception/main:" << e.what() << std::endl;
    }
}

आउटपुट:

In ~Foo: Commit
In main: Rollback
exception/main:Error

कस्टम अपवाद

आपको अपवाद के रूप में कच्चे मूल्यों को नहीं फेंकना चाहिए, इसके बजाय मानक अपवाद वर्गों में से एक का उपयोग करें या अपना खुद का बनाएं।

अपने स्वयं के अपवाद वर्ग को std::exception से प्राप्त होना std::exception बारे में जाने का एक अच्छा तरीका है। यहाँ एक कस्टम अपवाद वर्ग है जो सीधे std::exception :

#include <exception>

class Except: virtual public std::exception {
    
protected:

    int error_number;               ///< Error number
    int error_offset;               ///< Error offset
    std::string error_message;      ///< Error message
    
public:

    /** Constructor (C++ STL string, int, int).
     *  @param msg The error message
     *  @param err_num Error number
     *  @param err_off Error offset
     */
    explicit 
    Except(const std::string& msg, int err_num, int err_off):
        error_number(err_num),
        error_offset(err_off),
        error_message(msg)
        {}

    /** Destructor.
     *  Virtual to allow for subclassing.
     */
    virtual ~Except() throw () {}

    /** Returns a pointer to the (constant) error description.
     *  @return A pointer to a const char*. The underlying memory
     *  is in possession of the Except object. Callers must
     *  not attempt to free the memory.
     */
    virtual const char* what() const throw () {
       return error_message.c_str();
    }
    
    /** Returns error number.
     *  @return #error_number
     */
    virtual int getErrorNumber() const throw() {
        return error_number;
    }
    
    /**Returns error offset.
     * @return #error_offset
     */
    virtual int getErrorOffset() const throw() {
        return error_offset;
    }

};

एक उदाहरण फेंक पकड़:

try {
    throw(Except("Couldn't do what you were expecting", -12, -34));
} catch (const Except& e) {
    std::cout<<e.what()
             <<"\nError number: "<<e.getErrorNumber()
             <<"\nError offset: "<<e.getErrorOffset();
}

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

एक अपवाद वर्ग है कि आप त्रुटि संदेशों को अच्छी तरह से संभालते हैं: std::runtime_error

आप इस वर्ग से भी विरासत में मिल सकते हैं:

#include <stdexcept>

class Except: virtual public std::runtime_error {
    
protected:

    int error_number;               ///< Error number
    int error_offset;               ///< Error offset
    
public:

    /** Constructor (C++ STL string, int, int).
     *  @param msg The error message
     *  @param err_num Error number
     *  @param err_off Error offset
     */
    explicit 
    Except(const std::string& msg, int err_num, int err_off):
        std::runtime_error(msg)
        {
            error_number = err_num;
            error_offset = err_off;
            
        }

    /** Destructor.
     *  Virtual to allow for subclassing.
     */
    virtual ~Except() throw () {}
    
    /** Returns error number.
     *  @return #error_number
     */
    virtual int getErrorNumber() const throw() {
        return error_number;
    }
    
    /**Returns error offset.
     * @return #error_offset
     */
    virtual int getErrorOffset() const throw() {
        return error_offset;
    }

};

ध्यान दें कि मैंने आधार वर्ग ( std::runtime_error ) से what() फ़ंक्शन को ओवरराइड नहीं किया है, अर्थात हम बेस क्लास के संस्करण का उपयोग what() । यदि आपके पास आगे का एजेंडा है तो आप इसे ओवरराइड कर सकते हैं।



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow