In C++, exception handling provides a way to manage runtime errors gracefully, ensuring that the program can handle unexpected situations without crashing. The C++ standard library uses three main keywords for exception handling:
- try: Block of code that may cause an exception.
- throw: Used to raise an exception.
- catch: Block that handles the exception if it is thrown.
Using these keywords, you can handle errors and exceptions in a controlled manner.
Basic Syntax
try { // Code that may throw an exception throw exception_type; // Raising an exception } catch (exception_type variable) { // Code to handle the exception }
1. Basic Example of Exception Handling
This example demonstrates a basic try-catch block.
#include <iostream> using namespace std; int main() { try { int x = 5; int y = 0; if (y == 0) { throw "Division by zero error!"; } cout << x / y << endl; } catch (const char* e) { cout << "Error: " << e << endl; } return 0; }
Explanation:
- throw “Division by zero error!”; raises an exception if y is 0.
- The catch block catches the exception and displays an error message.
Output:
Error: Division by zero error!
2. Throwing and Catching Integer Exceptions
You can throw and catch integer exceptions to handle errors more specifically.
#include <iostream> using namespace std; int main() { try { int value = -1; if (value < 0) { throw value; // Throw an integer exception } } catch (int e) { cout << "Error: Negative value encountered (" << e << ")" << endl; } return 0; }
Explanation:
- An integer exception is thrown with throw value if value is negative.
- The catch block receives the integer exception and displays the value.
Output:
Error: Negative value encountered (-1)
3. Catching Multiple Exception Types
You can have multiple catch blocks to handle different types of exceptions.
#include <iostream> using namespace std; int main() { try { int option = 2; if (option == 1) { throw "A string exception!"; } else if (option == 2) { throw 404; } } catch (const char* e) { cout << "String Exception: " << e << endl; } catch (int e) { cout << "Integer Exception: Error code " << e << endl; } return 0; }
Explanation:
- The try block throws different exceptions based on option.
- Multiple catch blocks handle each exception type accordingly.
Output:
Integer Exception: Error code 404
4. Catching All Exceptions with catch(…)
You can catch all exceptions using catch(…), which acts as a generic catch block.
#include <iostream> using namespace std; int main() { try { throw 3.14; // Throwing a double exception } catch (...) { cout << "An exception occurred" << endl; } return 0; }
Explanation:
- catch(…) catches any exception type, making it a catch-all block.
- This can be useful as a fallback if specific handlers aren’t defined.
Output:
An exception occurred
5. Exception Handling with Classes
You can define custom exception classes to represent specific error conditions.
#include <iostream> #include <string> using namespace std; class MyException { public: string message; MyException(string msg) : message(msg) {} }; int main() { try { throw MyException("Custom exception thrown!"); } catch (MyException& e) { cout << "Caught exception: " << e.message << endl; } return 0; }
Explanation:
- MyException is a custom exception class with a message attribute.
- The catch block catches exceptions of type MyException and displays the message.
Output:
Caught exception: Custom exception thrown!
6. Exception Handling in Functions
Functions can throw exceptions, which can be caught by the caller.
#include <iostream> using namespace std; void checkNumber(int number) { if (number < 0) { throw "Negative numbers are not allowed!"; } cout << "Number: " << number << endl; } int main() { try { checkNumber(-5); } catch (const char* e) { cout << "Error: " << e << endl; } return 0; }
Explanation:
- checkNumber throws an exception if number is negative.
- The exception is caught in main, where the function is called.
Output:
Error: Negative numbers are not allowed!
7. Using std::exception as a Base Class
The C++ standard library provides the std::exception class as a base for standard exceptions.
#include <iostream> #include <exception> using namespace std; class CustomException : public exception { public: const char* what() const noexcept override { return "Custom exception occurred!"; } }; int main() { try { throw CustomException(); } catch (exception& e) { cout << e.what() << endl; } return 0; }
Explanation:
- CustomException inherits from std::exception and overrides what() to provide a custom message.
- what() is called in the catch block to display the error message.
Output:
Custom exception occurred!
8. Nested Try-Catch Blocks
You can nest try-catch blocks, allowing more granular error handling.
#include <iostream> using namespace std; int main() { try { try { throw "Inner exception"; } catch (const char* e) { cout << "Caught in inner catch: " << e << endl; throw; // Rethrow to outer catch } } catch (...) { cout << "Caught in outer catch" << endl; } return 0; }
Explanation:
- The inner try block throws an exception, which is caught and rethrown to the outer try-catch.
- The outer catch block catches the rethrown exception.
Output:
Caught in inner catch: Inner exception Caught in outer catch
9. Rethrowing Exceptions
An exception caught in one catch block can be rethrown to be handled by an outer catch block.
#include <iostream> using namespace std; void func() { try { throw "Exception from func"; } catch (const char* e) { cout << "Caught in func: " << e << endl; throw; // Rethrow exception } } int main() { try { func(); } catch (const char* e) { cout << "Caught in main: " << e << endl; } return 0; }
Explanation:
- func catches and then rethrows an exception, which is later caught in main.
Output:
Caught in func: Exception from func Caught in main: Exception from func
10. Using std::terminate with Uncaught Exceptions
If an exception is thrown but not caught, the program calls std::terminate.
#include <iostream> #include <exception> using namespace std; void customTerminate() { cout << "Uncaught exception. Terminating program..." << endl; exit(1); } int main() { set_terminate(customTerminate); try { throw 1; } catch (const char* e) { cout << "Caught exception: " << e << endl; } return 0; }
Explanation:
- set_terminate(customTerminate) sets a custom handler for uncaught exceptions.
- Since no catch block handles the integer exception, customTerminate is called.
Output:
Uncaught exception. Terminating program...
11. Exception Specification (noexcept)
You can use noexcept to specify that a function does not throw exceptions.
#include <iostream> using namespace std; void func() noexcept { cout << "This function does not throw exceptions." << endl; } int main() { try { func(); } catch (...) { cout << "Caught exception" << endl; } return 0; }
Explanation:
- noexcept indicates that func does not throw exceptions.
- This can be useful for optimization and ensuring that certain functions are exception-free.
Output:
This function does not throw exceptions.
Summary Table of Exception Handling Examples
Example | Description |
---|---|
Basic Exception Handling | Simple try-catch with throw |
Integer Exception | Throwing and catching integer exceptions |
Multiple Exception Types | Using multiple catch blocks for different types |
Catching All Exceptions | Using catch(...) to catch any exception |
Custom Exception Class | Defining and catching a custom exception class |
Exception Handling in Functions | Throwing exceptions from functions |
std::exception Base Class |
Using std::exception as a base class for custom exceptions |
Nested Try-Catch Blocks | Handling exceptions with nested try-catch |
Rethrowing Exceptions | Rethrowing exceptions for outer handling |
Using std::terminate |
Handling uncaught exceptions with std::terminate |
noexcept Specification |
Indicating that a function does not throw exceptions |
Key Takeaways
- Exception handling in C++ helps manage runtime errors gracefully.
- try-throw-catch mechanism: try encloses risky code, throw raises exceptions, and catch handles them.
- You can create custom exception classes to represent specific errors.
- Multiple catch blocks allow handling different exception types, and catch(…) provides a catch-all handler.
- noexcept specifies that a function does not throw exceptions, which can improve performance.
- Best practices include defining meaningful custom exceptions, rethrowing exceptions where appropriate, and avoiding using exceptions for regular control flow.