0
0
C++programming~15 mins

Multiple catch blocks in C++ - Deep Dive

Choose your learning style9 modes available
Overview - Multiple catch blocks
What is it?
Multiple catch blocks allow a program to handle different types of errors separately. When an error happens, the program looks for a catch block that matches the error type and runs it. This way, you can respond differently depending on what went wrong. It helps keep your program safe and clear.
Why it matters
Without multiple catch blocks, a program would treat all errors the same way, which can cause confusion or wrong fixes. Handling errors specifically helps programs recover gracefully or give clear messages. This improves user experience and prevents crashes or data loss.
Where it fits
Before learning multiple catch blocks, you should understand basic try and catch error handling. After this, you can learn about custom exceptions and exception hierarchies to organize error types better.
Mental Model
Core Idea
Multiple catch blocks let a program pick the right response for each kind of error it encounters.
Think of it like...
Imagine a customer service desk with different specialists: one handles billing issues, another handles technical problems, and another handles returns. When a customer comes with a problem, they are directed to the right specialist who knows how to fix it.
┌─────────────┐
│   try block │
└──────┬──────┘
       │ throws exception
       ▼
┌─────────────┐
│ catch(Type1)│
├─────────────┤
│ catch(Type2)│
├─────────────┤
│ catch(...)  │
└─────────────┘
Program picks the first matching catch block.
Build-Up - 6 Steps
1
FoundationBasic try and catch usage
🤔
Concept: Learn how to use try and catch to handle a single error type.
In C++, you write code that might fail inside a try block. If an error happens, the catch block runs to handle it. Example: try { int x = 10 / 0; // This causes an error } catch (const std::exception& e) { std::cout << "Error caught: " << e.what() << std::endl; }
Result
The program catches the division error and prints a message instead of crashing.
Understanding the basic try-catch structure is essential before handling multiple error types.
2
FoundationException types and throwing
🤔
Concept: Learn how to throw different types of exceptions to signal different errors.
You can throw different objects to represent errors: throw 5; // throws an int throw "error"; // throws a string throw std::runtime_error("fail"); // throws an exception object Each type can be caught separately.
Result
Different thrown types allow the program to distinguish error causes.
Knowing how to throw different types sets the stage for catching them separately.
3
IntermediateMultiple catch blocks syntax
🤔
Concept: Learn how to write multiple catch blocks to handle different exception types.
You can write several catch blocks after one try block: try { // code that may throw } catch (int e) { std::cout << "Caught int: " << e << std::endl; } catch (const char* msg) { std::cout << "Caught string: " << msg << std::endl; } catch (...) { std::cout << "Caught unknown exception" << std::endl; }
Result
The program runs the catch block matching the thrown exception type.
Multiple catch blocks let you respond differently to each error type, improving clarity and control.
4
IntermediateOrder of catch blocks matters
🤔Before reading on: Do you think catch blocks order affects which one runs? Commit to your answer.
Concept: Catch blocks are checked in order; the first matching one runs, so order affects behavior.
If you put a catch-all block (catch(...)) before specific ones, it will catch all exceptions first, blocking others. Example: try { throw 10; } catch (...) { std::cout << "Catch all" << std::endl; } catch (int e) { std::cout << "Catch int" << std::endl; } Output will be "Catch all" because it comes first.
Result
The first matching catch block runs; later ones are ignored for that exception.
Knowing catch block order prevents bugs where specific errors are hidden by general handlers.
5
AdvancedCatching by reference vs by value
🤔Before reading on: Should exceptions be caught by value or by reference? Commit to your answer.
Concept: Catching exceptions by reference avoids slicing and preserves polymorphism.
Catching by value copies the exception object, which can slice derived classes to base class parts. Example: try { throw std::runtime_error("error"); } catch (std::exception e) { // caught by value std::cout << e.what() << std::endl; } Better: catch (const std::exception& e) { // caught by reference std::cout << e.what() << std::endl; }
Result
Catching by reference preserves the full exception object and its behavior.
Understanding catch by reference avoids subtle bugs with exception object slicing.
6
ExpertException matching and inheritance rules
🤔Before reading on: Does a catch block for a base class catch derived exceptions? Commit to your answer.
Concept: Catch blocks match exceptions by type, including inheritance; base class catch blocks catch derived exceptions unless a derived catch block appears first.
If you throw a derived exception, a catch block for its base class will catch it if no derived catch block matches first. Example: class Base : public std::exception {}; class Derived : public Base {}; try { throw Derived(); } catch (const Base& b) { std::cout << "Caught Base or Derived" << std::endl; } catch (const Derived& d) { std::cout << "Caught Derived" << std::endl; } Output: "Caught Base or Derived" because Base catch comes first.
Result
Catch block order and inheritance affect which handler runs for derived exceptions.
Knowing inheritance rules helps design catch blocks to handle exceptions precisely and avoid unintended catches.
Under the Hood
When an exception is thrown, the C++ runtime searches the call stack for a matching catch block. It compares the thrown exception's type with each catch block's type in order. The first compatible catch block runs, and the stack unwinds to that point. Catching by reference avoids copying the exception object, preserving its full type and data.
Why designed this way?
This design allows flexible and precise error handling. Using multiple catch blocks lets programmers separate error responses by type. Catching by reference prevents object slicing, preserving polymorphism. The order of catch blocks ensures predictable matching, avoiding ambiguity.
┌─────────────┐
│ throw excpt │
└──────┬──────┘
       │ runtime searches stack
       ▼
┌─────────────────────────────┐
│ catch(Type1) ?              │
├─────────────────────────────┤
│ catch(Type2) ?              │
├─────────────────────────────┤
│ catch(...) ?                │
└─────────────┬───────────────┘
              │ first match runs
              ▼
       Exception handled
Myth Busters - 4 Common Misconceptions
Quick: Does catch(...) catch exceptions before specific types if placed first? Commit yes or no.
Common Belief:catch(...) always runs last and never blocks other catch blocks.
Tap to reveal reality
Reality:catch(...) catches all exceptions and runs immediately if placed first, blocking later catch blocks.
Why it matters:Placing catch(...) first can hide specific error handlers, causing wrong or generic error handling.
Quick: Can you catch exceptions by value without issues? Commit yes or no.
Common Belief:Catching exceptions by value is fine and equivalent to catching by reference.
Tap to reveal reality
Reality:Catching by value copies the exception and can slice derived class info, losing details.
Why it matters:Slicing causes loss of error details and polymorphic behavior, leading to incorrect handling.
Quick: Does the order of catch blocks not affect which one runs? Commit yes or no.
Common Belief:Catch blocks order does not matter; the runtime finds the best match regardless.
Tap to reveal reality
Reality:Catch blocks are checked in order; the first matching one runs, so order matters.
Why it matters:Wrong order can cause general handlers to catch exceptions before specific ones, hiding them.
Quick: Does a catch block for a base class catch derived exceptions? Commit yes or no.
Common Belief:A catch block for a base class cannot catch derived exceptions.
Tap to reveal reality
Reality:A catch block for a base class catches derived exceptions unless a derived catch block appears first.
Why it matters:Misunderstanding this leads to unexpected catch block execution and error handling bugs.
Expert Zone
1
Catching exceptions by const reference is best practice to avoid copying and allow polymorphism.
2
Using catch(...) as a last resort helps catch unexpected exceptions but should not replace specific handlers.
3
Exception specifications (deprecated in modern C++) once influenced catch behavior but are now replaced by noexcept.
When NOT to use
Multiple catch blocks are not suitable when you want uniform error handling; in such cases, a single catch block or error codes might be better. For performance-critical code, exceptions might be avoided altogether in favor of error returns.
Production Patterns
In real systems, multiple catch blocks handle different error categories like IO errors, logic errors, or network failures separately. Logging and cleanup code often go in specific catch blocks. Sometimes, catch blocks rethrow exceptions after partial handling.
Connections
Polymorphism in Object-Oriented Programming
Multiple catch blocks rely on polymorphism to catch derived exception types via base class references.
Understanding polymorphism helps grasp how catch blocks for base classes can handle derived exceptions, enabling flexible error handling.
HTTP Status Codes
Like multiple catch blocks, HTTP status codes let servers respond differently to various client errors or successes.
Knowing how servers use specific codes to signal different outcomes helps understand why programs use multiple catch blocks for precise error responses.
Medical Triage Systems
Both systems prioritize handling based on severity or type, directing cases to the right specialist or handler.
Recognizing this pattern across domains shows how categorizing problems improves response efficiency and effectiveness.
Common Pitfalls
#1Placing catch(...) before specific catch blocks.
Wrong approach:try { // code } catch (...) { std::cout << "Catch all" << std::endl; } catch (int e) { std::cout << "Catch int" << std::endl; }
Correct approach:try { // code } catch (int e) { std::cout << "Catch int" << std::endl; } catch (...) { std::cout << "Catch all" << std::endl; }
Root cause:Misunderstanding that catch blocks are checked in order, so catch(...) first blocks others.
#2Catching exceptions by value causing slicing.
Wrong approach:try { throw std::runtime_error("error"); } catch (std::exception e) { std::cout << e.what() << std::endl; }
Correct approach:try { throw std::runtime_error("error"); } catch (const std::exception& e) { std::cout << e.what() << std::endl; }
Root cause:Not realizing that catching by value copies and slices the exception object.
#3Assuming catch block order does not affect which runs.
Wrong approach:try { throw Derived(); } catch (const Base& b) { std::cout << "Base catch" << std::endl; } catch (const Derived& d) { std::cout << "Derived catch" << std::endl; }
Correct approach:try { throw Derived(); } catch (const Derived& d) { std::cout << "Derived catch" << std::endl; } catch (const Base& b) { std::cout << "Base catch" << std::endl; }
Root cause:Not understanding that catch blocks are checked in order, so base class catch before derived hides derived catch.
Key Takeaways
Multiple catch blocks let you handle different error types separately, improving program clarity and safety.
Catch blocks are checked in order; the first matching one runs, so order matters a lot.
Always catch exceptions by const reference to avoid slicing and preserve full error information.
The catch(...) block catches all exceptions but should be placed last to avoid hiding specific handlers.
Understanding inheritance in exceptions helps design catch blocks that handle errors precisely and predictably.