Exception Handling
When a program encounters an unexpected problem, it should respond safely instead of terminating unexpectedly. In this lesson, you will learn how C++ uses try, throw, and catch to detect and handle exceptional situations.
Introduction
In the previous lesson, you learned about namespaces. Now consider a program that asks the user to enter two numbers and performs a calculation.
Suppose the user enters 20 as the numerator and 0 as the denominator. The program attempts to perform integer division by zero, which is an invalid operation and results in undefined behavior.
int numerator = 20;
int denominator = 0;
int result =
numerator / denominator;Programs can also encounter many other exceptional situations, such as invalid input, unavailable files, failed network operations, or insufficient resources.
Unhandled Problem
- An exceptional condition occurs.
- The program does not respond properly.
- The operation cannot continue normally.
- The application may terminate unexpectedly.
Handled Exception
- The exceptional condition is detected.
- An exception is thrown.
- A matching handler responds.
- The program handles the situation safely.
Exception handling provides a structured way to transfer control from code that detects an exceptional condition to code designed to handle it.
What is Exception Handling?
Exception handling is a mechanism for reporting and handling exceptional situations that occur while a program is running.
Instead of mixing every possible error response directly into normal program logic, C++ allows code to throw an exception and transfer control to a matching handler.
Normal Code
│
▼
Exceptional Condition
│
▼
Throw Exception
│
▼
Find Matching Handler
│
▼
Handle Situation| Concept | Meaning |
|---|---|
| Exception | An object or value used to report an exceptional condition |
| Throw | Signals an exception |
| Handler | Code that catches and handles an exception |
| try Block | Contains code associated with exception handlers |
| catch Block | Handles a matching exception |
Exception handling allows one part of a program to report an exceptional situation and another part to handle it.
Why Do We Need Exception Handling?
Real applications interact with users, files, networks, databases, memory, devices, and external systems. Many operations can fail for reasons that are not part of the normal successful path.
Without Structured Handling
- Error checks may be scattered everywhere.
- Normal logic becomes harder to read.
- Failures may be ignored.
- Unexpected termination becomes more likely.
- Errors may be difficult to propagate.
With Exception Handling
- Exceptional situations can be reported clearly.
- Handlers can be separated from normal logic.
- Errors can propagate to appropriate levels.
- Different exception types can be handled differently.
- Applications can respond more deliberately.
Controlled Error Handling
Exceptional situations can be handled by dedicated code.
Cleaner Normal Logic
The successful execution path can remain easier to follow.
Error Propagation
Exceptions can move through function calls until a suitable handler is found.
Type-Based Handling
Different exception types can have different handlers.
Better Diagnostics
Meaningful exception information can help explain failures.
Robust Applications
Programs can respond deliberately to recoverable and unrecoverable situations.
Real-World Analogy
Imagine using an ATM. You insert your card and enter a PIN.
If the PIN is correct, the ATM continues with the transaction. If the PIN is incorrect, the machine does not simply shut down. It detects the problem and follows an alternative error-handling path.
ATM Transaction
│
▼
Enter PIN
│
┌───────┴───────┐
▼ ▼
Correct Incorrect
│ │
▼ ▼
Continue Handle Problem
│
▼
Display Error Message| ATM Process | Exception Handling |
|---|---|
| Transaction operation | Program operation |
| Check PIN | Execute potentially failing code |
| Invalid PIN detected | Exceptional condition detected |
| Report the problem | Throw an exception |
| Display error response | Catch and handle the exception |
Exception handling provides a separate path for exceptional situations instead of treating them as part of the normal successful flow.
Exception Handling Keywords
C++ exception handling commonly uses three keywords: try, throw, and catch.
1️⃣ try
Introduces a block of code associated with one or more exception handlers.
2️⃣ throw
Signals an exception and provides the exception object or value.
3️⃣ catch
Defines a handler for a matching exception type.
try
{
// Code that may throw
}
catch (ExceptionType value)
{
// Handle matching exception
}throw 100;| Keyword | Purpose |
|---|---|
| try | Associates a block with exception handlers |
| throw | Signals an exception |
| catch | Handles a matching exception |
Code executes in a try block, an exceptional condition can cause an exception to be thrown, and a matching catch handler processes it.
Exception Handling Flow
When code inside a try block throws an exception, normal execution of that try block stops. C++ searches for a matching handler.
Program Starts
│
▼
Enter try Block
│
▼
Execute Code
│
▼
Exception Thrown?
┌──┴──┐
│ │
No Yes
│ │
▼ ▼
Continue Stop Remaining try Code
│
▼
Search for Handler
│
▼
Matching catch Found
│
▼
Execute Handler| Situation | Result |
|---|---|
| No exception is thrown | The try block continues normally |
| An exception is thrown | Remaining statements in the try block are skipped |
| Matching handler exists | The matching catch block executes |
| No matching handler exists | The exception continues searching outward; if ultimately unhandled, the program terminates |
Once an exception is thrown, the remaining statements in that try block are not executed unless control later returns through some separate program path.
Example 1: Basic Exception Handling
The following program throws an integer value and handles it with a matching catch block.
#include <iostream>
int main()
{
try
{
throw 100;
}
catch (int number)
{
std::cout
<< "Exception: "
<< number;
}
return 0;
}How the Program Works
- Execution enters the try block.
- throw 100 creates an exception of type int.
- The remaining execution of the try block stops.
- C++ searches for a matching handler.
- catch (int number) matches the thrown int.
- The handler receives the value 100.
- The exception information is displayed.
throw 100
│
▼
Exception Type: int
│
▼
catch (int number)
│
▼
number = 100Exception: 100The catch handler is selected because its parameter type matches the type of the thrown exception.
Example 2: Division by Zero
For integer arithmetic, division by zero is invalid and results in undefined behavior. The program can check the denominator before performing the division and throw an exception when the operation is invalid.
#include <iostream>
int main()
{
int numerator = 20;
int denominator = 0;
try
{
if (denominator == 0)
{
throw "Division by zero error!";
}
std::cout
<< numerator / denominator;
}
catch (const char* message)
{
std::cout << message;
}
return 0;
}How the Program Works
- The denominator contains 0.
- The condition denominator == 0 becomes true.
- A string literal is thrown.
- The division statement is skipped.
- catch (const char* message) handles the exception.
- The error message is displayed.
denominator = 0
│
▼
denominator == 0?
│
Yes
│
▼
Throw Message
│
▼
Catch const char*
│
▼
Display ErrorDivision by zero error!C++ does not automatically convert every programming error into a catchable exception. In this example, the program explicitly checks the denominator and throws an exception before attempting invalid integer division.
Example 3: Multiple Catch Blocks
A try block can be followed by multiple catch handlers. This allows different exception types to be handled differently.
#include <iostream>
int main()
{
try
{
throw 25.5;
}
catch (int number)
{
std::cout
<< "Integer Exception";
}
catch (double number)
{
std::cout
<< "Double Exception";
}
return 0;
}throw 25.5
│
▼
Type: double
│
▼
catch (int)
│
No Match
│
▼
catch (double)
│
Match
│
▼
Execute Handler| Thrown Type | Handler | Matches? |
|---|---|---|
| double | catch (int) | No |
| double | catch (double) | Yes |
Double ExceptionAfter an exception is thrown, C++ selects a matching handler. Once that handler executes, later handlers associated with the same try block are not also executed.
Catch-All Handler
A catch-all handler uses catch (...) and can handle exceptions that were not matched by earlier handlers associated with the same try block.
catch (...)
{
std::cout
<< "Unknown exception occurred!";
}try
{
// Code that may throw
}
catch (int value)
{
// Handle int
}
catch (double value)
{
// Handle double
}
catch (...)
{
// Handle anything else
}Catch-All First
- General handler appears before specific handlers.
- It would handle exceptions before later handlers.
- Specific handling becomes unavailable.
- The ordering is incorrect.
Catch-All Last
- Specific handlers are checked first.
- Known exception types receive specific handling.
- Unknown types fall back to catch (...).
- The handler order is clear.
A catch-all handler must appear after the specific handlers associated with the same try block.
Program Execution Flow
The complete exception-handling flow depends on whether an exception is thrown and whether a matching handler can be found.
Program Starts
│
▼
Enter try Block
│
▼
Execute Statements
│
▼
Exception Thrown?
┌──┴──┐
│ │
No Yes
│ │
▼ ▼
Finish Stop Remaining
try try Statements
│ │
│ ▼
│ Search for Handler
│ │
│ ▼
│ Matching catch
│ │
│ ▼
│ Execute Handler
│ │
└────┬────┘
▼
Continue After
Handler Sequence| Step | What Happens |
|---|---|
| 1 | The program enters the try block |
| 2 | Statements execute normally |
| 3 | An exceptional condition may cause a throw |
| 4 | The remaining try statements are skipped |
| 5 | A matching handler is selected |
| 6 | The catch block executes |
| 7 | Execution continues after the handler sequence when appropriate |
If the matching handler is outside the current function, C++ may exit intervening function scopes while searching for the handler. Local automatic objects are destroyed during this process.
Real-World Applications
Exception handling is useful when operations can fail in ways that should be reported to another part of the program.
File Operations
Applications can report failures when required files or resources cannot be accessed.
Banking Systems
Business operations can report exceptional conditions such as rejected transactions.
Authentication Systems
Security components can report failures that higher-level code must handle.
Database Systems
Applications can respond to connection failures and operation errors.
Network Applications
Network layers can report failed connections, timeouts, and protocol problems.
Libraries
Reusable libraries can report failures without deciding how the final application should display them.
User Interface
│
▼
Business Logic
│
▼
Service Layer
│
▼
External Operation
│
▼
Exceptional Failure
│
▼
Exception Propagates
│
▼
Appropriate Layer Handles ItThe code that detects a problem may not be the best place to decide how the application should respond. Exceptions can transfer that responsibility to an appropriate handler.
Advantages of Exception Handling
Cleaner Control Flow
Normal logic can remain separate from exceptional handling paths.
Propagation
Failures can move through multiple function calls to an appropriate handler.
Specific Handling
Different exception types can receive different responses.
Better Diagnostics
Exception objects can carry useful information about failures.
Layer Separation
Lower-level code can report problems to higher-level application logic.
Controlled Recovery
Recoverable situations can be handled deliberately when appropriate.
- Separates normal logic from exceptional handling.
- Allows failures to propagate across function boundaries.
- Supports type-based handlers.
- Can improve error reporting.
- Allows higher-level code to decide how to respond.
- Works with object lifetime and stack unwinding.
- Supports standard and custom exception types.
- Helps structure failure-handling policies.
Use exceptions for failures and exceptional conditions, not as a replacement for ordinary loops, conditions, or expected control flow.
Common Beginner Mistakes
If an exception remains unhandled, the program ultimately terminates.
A handler that does not match the thrown exception will not handle it.
The remaining statements in the current try execution path are skipped after the exception is thrown.
The catch-all handler must come after specific handlers.
Expected conditions should usually use ordinary control flow.
Standard exception objects are generally caught by const reference to avoid unnecessary copying and slicing.
int main()
{
throw 100;
// No handler exists
}try
{
throw 10;
}
catch (double value)
{
// Does not handle the thrown int
}try
{
throw 10;
// Not executed
std::cout << "Hello";
}
catch (int value)
{
std::cout << value;
}// ❌ Incorrect
catch (...)
{
}
catch (int value)
{
}// ✅ Correct
catch (int value)
{
}
catch (...)
{
}Handler selection depends on C++ exception-matching rules. Do not assume ordinary numeric conversions will make unrelated catch parameter types match.
Best Practices
- Use exceptions for exceptional situations rather than ordinary program flow.
- Throw meaningful exception types.
- Prefer standard exception classes when they appropriately describe the failure.
- Create custom exception types when the application needs domain-specific information.
- Catch the most specific exception types before more general handlers.
- Place catch (...) last.
- Catch standard exception objects by const reference.
- Keep try blocks focused on operations whose exceptions you intend to handle.
- Do not silently ignore exceptions without a deliberate reason.
- Provide useful diagnostic information.
- Use RAII so resources are released automatically during stack unwinding.
- Avoid manual resource-management patterns that can leak when exceptions occur.
- Handle an exception only when the current layer can respond meaningfully.
- Allow exceptions to propagate when a higher layer is better suited to handle them.
- Do not throw exceptions from destructors during stack unwinding.
#include <iostream>
#include <stdexcept>
int main()
{
try
{
throw std::runtime_error(
"Operation failed"
);
}
catch (const std::exception& error)
{
std::cout << error.what();
}
return 0;
}try
{
// Operation
}
catch (const std::runtime_error& error)
{
// Handle specific runtime error
}
catch (const std::exception& error)
{
// Handle other standard exceptions
}
catch (...)
{
// Final fallback
}Poor Exception Design
- Throw arbitrary values everywhere.
- Catch every exception immediately.
- Ignore useful error information.
- Use exceptions for ordinary decisions.
Better Exception Design
- Use meaningful exception types.
- Handle errors at appropriate layers.
- Preserve diagnostic information.
- Use exceptions for exceptional failures.
Prefer objects such as standard containers, strings, smart pointers, and stream objects that manage their resources automatically. Their destructors participate in stack unwinding.
Frequently Asked Questions
What is an exception?
An exception is an object or value used to report an exceptional condition.
What is exception handling?
Exception handling is the mechanism used to throw, propagate, catch, and handle exceptional situations.
Which keywords are used for C++ exception handling?
The main keywords are try, throw, and catch.
What does a try block do?
A try block associates a group of statements with one or more exception handlers.
What does throw do?
throw signals an exception and transfers control away from the current normal execution path.
What does catch do?
A catch block defines a handler for a matching exception.
What happens to statements after throw?
The remaining statements in the current try execution path are skipped as exception handling begins.
Can a try block have multiple catch blocks?
Yes. Multiple handlers can process different exception types.
Do catch parameter types matter?
Yes. C++ uses exception-matching rules to select an appropriate handler.
What is catch (...)?
It is a catch-all handler that can handle exceptions not matched by earlier handlers.
Where should catch (...) be placed?
It should be placed after all specific handlers associated with the same try block.
What happens if no handler matches?
The exception continues searching through surrounding call contexts. If it remains unhandled, the program terminates.
Does division by zero automatically throw a C++ exception?
No. Integer division by zero is undefined behavior. A program must prevent the operation, for example by checking the denominator and explicitly throwing an exception.
What is stack unwinding?
Stack unwinding is the process of leaving function scopes while searching for a handler, destroying local automatic objects along the way.
Why catch standard exceptions by const reference?
It avoids unnecessary copying and helps preserve the dynamic type of polymorphic exception objects.
Should exceptions be used for normal program flow?
Generally no. Expected conditions are usually better handled with ordinary control structures.
Can we create custom exception classes?
Yes. Applications can define custom exception types for domain-specific failures.
What is std::exception?
std::exception is a standard base class used by many standard C++ exception types.
Can a program continue after handling an exception?
Yes. After a handler completes, execution can continue from the point following the associated handler sequence when the program structure allows it.
Why is exception handling useful in large applications?
It allows failures to propagate across layers and be handled where the application has enough context to respond appropriately.
Key Takeaways
- Exception handling manages exceptional situations.
- An exception is an object or value used to report a problem.
- The try keyword introduces code associated with handlers.
- The throw keyword signals an exception.
- The catch keyword defines an exception handler.
- Execution of the current try block stops when an exception is thrown.
- C++ searches for a matching handler.
- Handler selection depends on exception-matching rules.
- Multiple catch blocks can handle different exception types.
- Only the selected matching handler executes.
- catch (...) is a catch-all handler.
- The catch-all handler must appear after specific handlers.
- Unhandled exceptions ultimately cause program termination.
- Exceptions can propagate across function calls.
- Stack unwinding destroys local automatic objects while scopes are exited.
- Exception handling can separate normal logic from failure handling.
- Integer division by zero does not automatically produce a catchable C++ exception.
- Programs should validate dangerous operations before performing them.
- Standard exceptions are generally caught by const reference.
- Meaningful exception types improve diagnostics.
- Exceptions should not replace ordinary conditions and loops.
- Specific handlers should appear before general handlers.
- RAII helps resources remain safe when exceptions occur.
- Higher application layers can handle failures reported by lower layers.
- Custom exception types can represent domain-specific failures.
- Well-designed exception handling improves application structure and reliability.
Summary
Exception handling provides a structured mechanism for reporting and responding to exceptional situations in C++ programs.
The try keyword introduces code associated with handlers, throw signals an exception, and catch defines the code that handles a matching exception.
When an exception is thrown, the remaining statements in the current try block are skipped and C++ searches for a suitable handler. Different exception types can be handled by different catch blocks.
A catch-all handler written as catch (...) can process exceptions not matched by earlier handlers. It must appear after the specific handlers.
Exceptions can propagate through function calls. During this process, local automatic objects are destroyed as scopes are exited, a process known as stack unwinding.
Exception handling should be used for exceptional situations rather than ordinary control flow. Meaningful exception types, focused try blocks, appropriate handler placement, and RAII-based resource management help create safer designs.
C++ does not automatically turn every programming error into an exception. For example, integer division by zero is undefined behavior, so a program should detect the invalid denominator before performing the operation.
By understanding try, throw, catch, handler matching, catch-all handlers, propagation, and stack unwinding, you can design applications that respond to failures in a more controlled and maintainable way.
In the next lesson, you will learn about file handling and how C++ programs create, write, read, and manage files.