TIPWelcome to my CS205 lecture notes! Because the lecture is not in English, I will try my best to translate it.
And at the same time, the
PPT,lab-filealso use the English,I will write the English notes but not all.
NOTEIf you have a passion to konw more about the course, you can click the link below to learn more about the course. Read the repo.
Waiting for api.github.com...
WARNING由于本文篇幅过长,个人会添加适当的中文注解在里面。
Topic Overview
- Exceptions and Exception Handling:
- What is an exception?
- C++ exception handling mechanism:
try,throw,catch - Exception handling and resource management (RAII)
- Defining and Using Exception Classes:
- User-defined exception classes
- Handling exceptions from an inheritance hierarchy
- Order of
catchclauses
- C++ Standard Exceptions:
- Standard exception classes in
<stdexcept>header - The
std::exceptionclass and itswhat()method - The
noexceptspecifier - Throw by value, catch by reference
- Standard exception classes in
- Assertions:
assertmacro usage- Purpose and applicable scenarios for assertions
- Exercises
Foreword
Welcome to the Lab 14 study notes! This lab will introduce the exception handling mechanism in C++, a powerful way to deal with errors that allows programs to respond to unexpected situations (exceptions) at runtime. We will learn the usage of try, throw, and catch keywords, how to define and use custom exception classes, and how to leverage the exception classes provided by the C++ standard library. Additionally, we will explore assertions (assert) as a debugging tool.
Exceptions and Exception Handling
1.1 What is an exception?
An exception is an unexpected or anomalous situation that occurs during program execution, interrupting the normal flow of instructions. For example, division by zero, memory allocation failure, or a file not being found can all raise exceptions. If not handled, an exception typically leads to program termination.
Exception handling provides a structured way to detect and respond to these error conditions, enabling programs to run more robustly or at least fail in a controlled manner (e.g., by saving data before exiting).
1.2 C++ Exception Handling Mechanism: try, throw, catch

C++ provides the following three keywords to support exception handling:
try: Atryblock encloses code that might throw an exception. If code within atryblock throws an exception, control is immediately transferred to an appropriatecatchblock.throw: When an error condition is detected, athrowexpression can be used to “throw” an exception. What is thrown can be a value (like an integer or a string literal) or an object (typically an object of an exception class).catch: Acatchblock immediately follows atryblock and is used to catch and handle a specific type of exception. There can be multiplecatchblocks to handle different types of exceptions.catch(...)can catch any type of exception.
Basic Flow:
- Program execution enters a
tryblock. - If a
throwstatement is executed within thetryblock (or in a function called from thetryblock), an exception is thrown. - The program immediately exits the current execution path and starts looking for a
catchblock that matches the type of the thrown exception. catchblocks are examined in the order they appear. The firstcatchblock whose parameter type matches the thrown exception (or can be converted from the thrown exception type) is executed.- If a matching
catchblock is found, the code within that block is executed. After execution, the program typically continues with the code following the lastcatchblock (unless thecatchblock itself re-throws an exception or terminates the program). - If no matching
catchblock is found, the exception propagates up the call stack until it is caught somewhere, or if it reaches themainfunction and is still unhandled, the program usually callsstd::terminate()to halt.
Example 1: Throwing an exception in main’s try block
#include <iostream>
int main() {
int a = 5, b = 0;
double d;
try {
if (b == 0) {
throw "The divisor can not be zero!"; // Throwing a C-style string
}
d = static_cast<double>(a) / b;
std::cout << "The quotient of " << a << "/" << b << " is: " << d << std::endl;
} catch (const char* p_error) { // Catches const char* type exceptions
std::cerr << p_error << std::endl;
} catch (int error_code) { // Catches int type exceptions (won't match here)
std::cerr << "Exception code: " << error_code << std::endl;
}
return 0;
}
Example 2: Throwing an exception in another function, handled by catch in main
#include <iostream>
double Quotient(int a, int b) {
if (b == 0) {
throw 404; // Throwing an int type exception
}
return static_cast<double>(a) / b;
}
int main() {
int a = 5, b = 0;
double d;
try {
d = Quotient(a, b);
std::cout << "The quotient of " << a << "/" << b << " is: " << d << std::endl;
} catch (const char* p_error) {
std::cerr << p_error << std::endl;
} catch (int error_code) { // Matches the thrown int type exception
std::cerr << "Exception code: " << error_code << std::endl;
}
return 0;
}
NOTEIn general, no implicit type conversions are applied when matching exceptions to
catchclauses (though inheritance-based conversions are an exception, discussed later). If the thrown exception type DOSE NOT match anycatchclause’s parameter type, the program will terminate.
1.3 Exception Handling and Resource Management (RAII)
When an exception occurs, the code between the exception point in the try block and the beginning of the catch block (including destructors of objects in the current scope) is executed. This process is called stack unwinding. This is very important for automatic resource management.
RAII (Resource Acquisition Is Initialization) is a common C++ programming paradigm that binds the lifecycle of a resource to the lifecycle of an object. Resources are acquired during object construction and released during object destruction.
- If RAII is not used (e.g., manual
newfollowed bydeleteat the end of atryblock), an exception can prevent thedeletefrom being executed, leading to a resource leak.Polygon *p = new Rectangle(4,5); p->printarea(); // If an exception is thrown here delete p; // This line might not be executed - Solution 1: Clean up in
catchblock and re-throwPolygon *p = new Rectangle(4,5); try { p->printarea(); } catch (...) { delete p; // Clean up resource throw; // Re-throw the exception for higher-level handling } delete p; // Clean up if no exception occurred - Solution 2: Use Smart Pointers (Embodiment of RAII) Smart pointers (like
std::unique_ptrorstd::shared_ptr) automatically release the managed resource in their destructors. Even if an exception causes stack unwinding, the smart pointer’s destructor will be called, ensuring the resource is freed.std::shared_ptr<Polygon> sp(new Rectangle(10, 20)); sp->printarea(); // If an exception is thrown here, sp's destructor will still be called, // thereby deleting the Polygon object.
Defining and Using Exception Classes
While any type of value can be thrown, it is generally recommended to throw objects of class types, especially classes derived from std::exception or its descendants. Doing so allows carrying more information about the error.
2.1 User-Defined Exception Classes
We can define our own exception classes to represent specific error conditions.
#include <iostream>
#include <limits>
using namespace std;
class RangeError {
private:
int val;
public:
RangeError(int v) : val(v) {}
int getValue() const { return val; }
};
char to_char(int n){
if(n < numeric_limits<char>::min() || n > numeric_limits<char>::max()) {
throw RangeError(n);
}
return (char)n;
}
void gain(int n){
try{
char c = to_char(n);
cout << "The character representation of " << n << " is: " << c << endl;
} catch (const RangeError& e) {
cerr << "RangeError: Value " << e.getValue() << " is out of range for char type." << endl;
cerr << "Range is" << (int) numeric_limits<char>::min() << " to "
<< (int) numeric_limits<char>::max() << endl;
} catch (...) {
cerr << "An unexpected error occurred." << endl;
}
}
int main(){
gain(-130);
return 0;
}Compilers may issue warnings for such ordering.

C++ Standard Exceptions
The C++ standard library defines a hierarchy of standard exception classes in <stdexcept> and other headers. They all (directly or indirectly) derive from std::exception.
3.1 The std::exception Class and its what() Method
std::exception is the base class for all standard C++ exceptions. It is defined in the <exception> header.
- It has an important virtual member function:
virtual const char* what() const noexcept;(orthrow()in older standards). - The
what()method returns a C-style string describing the exception. Derived classes typically override this method to provide more specific error information.
Example Standard Exception Class Hierarchy (Partial):
| Exception | Description |
|---|---|
std::exception | An exception and parent class of all the standard C++ exceptions. |
std::bad_alloc | This can be thrown by new. |
std::bad_cast | This can be thrown by dynamic_cast. |
std::bad_exception | Useful to handle unexpected exceptions in a C++ program. |
std::bad_typeid | This can be thrown by typeid. |
std::logic_error | An exception that theoretically can be detected by reading the code. |
std::domain_error | Thrown when a mathematically invalid domain is used. |
std::invalid_argument | Thrown due to invalid arguments. |
std::length_error | Thrown when a too big std::string is created. |
std::out_of_range | Can be thrown by the at method of std::vector and std::bitset. |
std::runtime_error | An exception that theoretically cannot be detected by reading the code. |
std::overflow_error | Thrown if a mathematical overflow occurs. |
std::range_error | Occurs when you try to store a value which is out of range. |
std::underflow_error | Thrown if a mathematical underflow occurs. |

Custom Exception Class Inheriting from std::exception:
#include <iostream>
#include <exception> // For std::exception
#include <string> // For std::string
class MyException : public std::exception {
private:
std::string m_msg;
public:
MyException(const std::string& msg) : m_msg(msg) {}
// Override the what() method
// C++11 and later recommend using noexcept
const char* what() const noexcept override {
return m_msg.c_str();
}
};
int main() {
try {
throw MyException("Custom C++ Exception.");
} catch (const MyException& me) { Catch derived class first
std::cout << "MyException is caught." << std::endl;
std::cout << me.what() << std::endl;
} catch (const std::exception& e) { // Then catch base class
std::cout << "Base class exception is caught." << std::endl;
std::cout << e.what() << std::endl;
}
return 0;
}

3.2 The noexcept Specifier (C++11)
noexcept is a function specifier that indicates that the function guarantees not to throw any exceptions.
void func() noexcept;// func will not throw exceptionsvoid func() noexcept(expression);// If expression is true, func will not throw exceptionsnoexcept(equivalent tonoexcept(true)) andthrow()(deprecated in C++17, removed in C++20) are equivalent.- If a function declared
noexceptactually throws an exception, the program callsstd::terminate(). - Destructors,
deleteoperators, and move operations (move constructors, move assignment operators) are implicitlynoexceptunless explicitly specified otherwise or if functions they call might throw.
3.3 Throw by Value, Catch by Reference
This is the recommended practice for handling exceptions:
- Throw by Value:
throw MyException("Error");- This creates a copy (or move, if applicable) of the exception object to be passed to the
catchblock.
- This creates a copy (or move, if applicable) of the exception object to be passed to the
- Catch by Reference:
catch (const MyException& e)- Avoids Object Slicing: If a base class exception is caught by value (
catch (std::exception e)), and a derived class object was actually thrown, the derived-specific part of the object will be “sliced off.”ewill only contain the base class part. Calling virtual functions (likee.what()) will call the base class version. - Avoids Unnecessary Copying: Catching by reference avoids another copy of the exception object.
- Usually, catch a
constreference, as thecatchblock should not modify the exception object.
- Avoids Object Slicing: If a base class exception is caught by value (


Assertions
Assertions (assert) are a debugging aid used to check conditions in code that the programmer believes should always be true.
4.1 assert Macro Usage
assert is a macro defined in <cassert> (C++) or <assert.h> (C).
Syntax:
assert(expression);Behavior:
- If
expressionevaluates to false (0),assertoutputs a message to the standard error stream (stderr) indicating the failed expression, source filename, and line number, and then callsstd::abort()to terminate the program. - If
expressionis true (non-zero),assertdoes nothing.
#include <cassert> // Or #include <assert.h> #include <iostream>
- If
4.2 Purpose and Applicable Scenarios for Assertions
- Purpose: Primarily used during development and debugging to catch logical errors or situations that should never occur. They validate the internal state of the program and the programmer’s assumptions.
- Applicable Scenarios:
- Checking the validity of function arguments (preconditions).
- Checking the validity of function return values (postconditions).
- Checking loop invariants.
- Verifying logically impossible situations in code.
- Considerations:
Each
assertshould ideally test only one condition, so it’s clear which condition failed if the assertion triggers.// Not recommended assert(nOffset >= 0 && nOffset + nSize <= m_nInformationSize); // Recommended assert(nOffset >= 0); assert(nOffset + nSize <= m_nInformationSize);- All
assertcalls can be removed at compile time by defining the macroNDEBUG(No Debug) before including<cassert>. This is typically done for release builds to avoid performance overhead and program termination due to assertions.#define NDEBUG // Place before #include <cassert> #include <cassert> - Assertions should not be used to handle expected runtime errors (like user input errors, file not found, etc.), which should be handled by exception handling or other error-handling mechanisms. Assertions are for programmer errors.
- All
Exercises
Exercise 1
Are there any warnings in the program below when it is compiled? What do the warnings mean? Run the program regardless of warnings and explain the result to the TA. Fix the warnings and run the program again.
#include <iostream>
#include <string>
#include <exception> // For std::exception
// using namespace std; // Avoid in global scope
class MyException : public std::exception {
public:
MyException(const std::string& msg) : m_msg(msg) {
std::cout << "MyException::MyException - set m_msg to:" << m_msg << std::endl;
}
~MyException() noexcept { // Destructors should generally be noexcept
std::cout << "MyException::~MyException" << std::endl;
}
// what() should also be noexcept and marked override
virtual const char* what() const noexcept override {
std::cout << "MyException::what" << std::endl;
return m_msg.c_str();
}
const std::string m_msg; // Better to make private and access via getter
};
void throwDerivedException() {
std::cout << "throwDerivedException - thrown a derived exception" << std::endl;
std::string exceptionMessage("MyException thrown");
throw MyException(exceptionMessage); // Throw by value
}
void illustrateDerivedExceptionCatch() {
std::cout << "illustrateDerivedExceptionsCatch - start" << std::endl;
try {
throwDerivedException();
}
// Warning: Base class catch block before derived class makes derived catch unreachable
catch (const std::exception& e) { // Base class catch block
std::cout << "illustrateDerivedExceptionsCatch - caught an std::exception, e.what:" << e.what() << std::endl;
}
// This catch block will never be executed because MyException will be caught by std::exception above
catch (const MyException& e) { // Derived class catch block
std::cout << "illustrateDerivedExceptionsCatch - caught a MyException, e.what::" << e.what() << std::endl;
}
std::cout << "illustrateDerivedExceptionsCatch - end" << std::endl;
}
int main(int argc, char** argv) {
std::cout << "main - start" << std::endl;
illustrateDerivedExceptionCatch();
std::cout << "main - end" << std::endl;
return 0;
}Hints for Solution:
- Warning: The compiler will warn that the
catchblock for the derived classMyExceptionis unreachable because it appears after thecatchblock for its base classstd::exception. - Runtime Result (with warning):
MyExceptionwill be caught bycatch (const std::exception& e)becauseMyExceptionis a derived class ofstd::exception.e.what()will correctly callMyException::what()due to virtual function dispatch. - Fix: Move the
catchblock forMyExceptionbefore thecatchblock forstd::exception.
Exercise 2
Define a generic class “GoldenRectangle” for a golden rectangle in the “golden_rectangle. h” , When testing based on the following main methods, the test results are shown in the bottom figure.
Except:

CC BY NC SA (Content adapted from course materials)


