cpp
Exceptions
02_Exceptions⚙️cpp
/**
* ============================================================
* C++ EXCEPTION HANDLING
* ============================================================
*
* This file covers:
* - try, catch, throw basics
* - Standard exceptions
* - Custom exception classes
* - Exception specifications
* - RAII and exceptions
* - Best practices
*
* Compile: g++ -std=c++17 -Wall 01_exceptions.cpp -o exceptions
* Run: ./exceptions
*
* ============================================================
*/
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <memory>
#include <fstream>
using namespace std;
// ============================================================
// PART 1: BASIC EXCEPTION HANDLING
// ============================================================
double divide(double a, double b) {
if (b == 0) {
throw runtime_error("Division by zero!");
}
return a / b;
}
int getElement(const vector<int>& vec, int index) {
if (index < 0 || index >= static_cast<int>(vec.size())) {
throw out_of_range("Index " + to_string(index) + " is out of range");
}
return vec[index];
}
// ============================================================
// PART 2: CUSTOM EXCEPTION CLASSES
// ============================================================
// Simple custom exception
class FileException : public exception {
private:
string message;
public:
FileException(const string& msg) : message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
};
// Exception with additional data
class ValidationException : public exception {
private:
string field;
string value;
string message;
mutable string fullMessage;
public:
ValidationException(const string& f, const string& v, const string& msg)
: field(f), value(v), message(msg) {}
const char* what() const noexcept override {
fullMessage = "Validation error in '" + field + "': " + message +
" (value: '" + value + "')";
return fullMessage.c_str();
}
const string& getField() const { return field; }
const string& getValue() const { return value; }
};
// Exception hierarchy
class DatabaseException : public runtime_error {
public:
DatabaseException(const string& msg) : runtime_error(msg) {}
};
class ConnectionException : public DatabaseException {
public:
ConnectionException(const string& host)
: DatabaseException("Could not connect to: " + host) {}
};
class QueryException : public DatabaseException {
private:
string query;
public:
QueryException(const string& q, const string& error)
: DatabaseException("Query failed: " + error), query(q) {}
const string& getQuery() const { return query; }
};
// ============================================================
// PART 3: FUNCTIONS THAT THROW
// ============================================================
void processFile(const string& filename) {
ifstream file(filename);
if (!file) {
throw FileException("Could not open file: " + filename);
}
string line;
int lineNum = 0;
while (getline(file, line)) {
lineNum++;
if (line.empty()) {
throw runtime_error("Empty line at line " + to_string(lineNum));
}
}
}
void validateEmail(const string& email) {
if (email.empty()) {
throw ValidationException("email", email, "cannot be empty");
}
if (email.find('@') == string::npos) {
throw ValidationException("email", email, "must contain @");
}
if (email.find('.') == string::npos) {
throw ValidationException("email", email, "must contain .");
}
}
void connectToDatabase(const string& host, bool simulateFail = true) {
if (simulateFail) {
throw ConnectionException(host);
}
}
// ============================================================
// PART 4: RAII AND EXCEPTIONS
// ============================================================
class Resource {
private:
string name;
public:
Resource(const string& n) : name(n) {
cout << " Resource '" << name << "' acquired" << endl;
}
~Resource() {
cout << " Resource '" << name << "' released" << endl;
}
void use() {
cout << " Using resource '" << name << "'" << endl;
}
};
// Smart pointer for exception-safe resource management
class SafeOperation {
private:
unique_ptr<Resource> resource;
public:
SafeOperation(const string& name)
: resource(make_unique<Resource>(name)) {}
void operate(bool shouldThrow = false) {
resource->use();
if (shouldThrow) {
throw runtime_error("Operation failed!");
}
}
// Resource automatically cleaned up even if exception thrown
};
// ============================================================
// MAIN FUNCTION
// ============================================================
int main() {
cout << "============================================" << endl;
cout << " C++ EXCEPTION HANDLING" << endl;
cout << "============================================" << endl << endl;
// ========================================================
// DEMO 1: Basic try-catch
// ========================================================
cout << "--- DEMO 1: BASIC TRY-CATCH ---" << endl << endl;
// Successful operation
try {
double result = divide(10, 2);
cout << "10 / 2 = " << result << endl;
} catch (const runtime_error& e) {
cout << "Error: " << e.what() << endl;
}
// Exception thrown
try {
double result = divide(10, 0);
cout << "10 / 0 = " << result << endl; // Never reached
} catch (const runtime_error& e) {
cout << "Error: " << e.what() << endl;
}
cout << "\nProgram continues after exception handled!" << endl;
cout << endl;
// ========================================================
// DEMO 2: Multiple catch blocks
// ========================================================
cout << "--- DEMO 2: MULTIPLE CATCH BLOCKS ---" << endl << endl;
vector<int> vec = {1, 2, 3, 4, 5};
auto tryAccess = [&vec](int index) {
try {
int value = getElement(vec, index);
cout << "vec[" << index << "] = " << value << endl;
} catch (const out_of_range& e) {
cout << "Out of range: " << e.what() << endl;
} catch (const exception& e) {
cout << "Exception: " << e.what() << endl;
} catch (...) { // Catch all
cout << "Unknown exception!" << endl;
}
};
tryAccess(2);
tryAccess(10);
tryAccess(-1);
cout << endl;
// ========================================================
// DEMO 3: Standard Exceptions
// ========================================================
cout << "--- DEMO 3: STANDARD EXCEPTIONS ---" << endl << endl;
cout << "Standard exception hierarchy:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "exception" << endl;
cout << "├── logic_error" << endl;
cout << "│ ├── invalid_argument" << endl;
cout << "│ ├── domain_error" << endl;
cout << "│ ├── length_error" << endl;
cout << "│ └── out_of_range" << endl;
cout << "├── runtime_error" << endl;
cout << "│ ├── range_error" << endl;
cout << "│ ├── overflow_error" << endl;
cout << "│ └── underflow_error" << endl;
cout << "└── bad_alloc, bad_cast, etc." << endl;
// Throwing standard exceptions
try {
throw invalid_argument("Invalid argument provided");
} catch (const invalid_argument& e) {
cout << "\nCaught invalid_argument: " << e.what() << endl;
}
try {
throw length_error("Length exceeds maximum");
} catch (const length_error& e) {
cout << "Caught length_error: " << e.what() << endl;
}
cout << endl;
// ========================================================
// DEMO 4: Custom Exception Classes
// ========================================================
cout << "--- DEMO 4: CUSTOM EXCEPTIONS ---" << endl << endl;
// Simple custom exception
try {
throw FileException("config.ini not found");
} catch (const FileException& e) {
cout << "FileException: " << e.what() << endl;
}
// Exception with data
try {
validateEmail("invalid-email");
} catch (const ValidationException& e) {
cout << "\nValidationException:" << endl;
cout << " " << e.what() << endl;
cout << " Field: " << e.getField() << endl;
cout << " Value: " << e.getValue() << endl;
}
// Exception hierarchy
try {
connectToDatabase("localhost:5432");
} catch (const ConnectionException& e) {
cout << "\nConnectionException: " << e.what() << endl;
} catch (const DatabaseException& e) {
cout << "\nDatabaseException: " << e.what() << endl;
}
cout << endl;
// ========================================================
// DEMO 5: Rethrowing Exceptions
// ========================================================
cout << "--- DEMO 5: RETHROWING ---" << endl << endl;
auto middleFunction = []() {
try {
divide(1, 0);
} catch (const exception& e) {
cout << "Middle: Logging error: " << e.what() << endl;
throw; // Rethrow same exception
}
};
try {
middleFunction();
} catch (const exception& e) {
cout << "Outer: Handling error: " << e.what() << endl;
}
// Wrapping exception
auto wrapException = []() {
try {
throw runtime_error("Original error");
} catch (const runtime_error& e) {
throw DatabaseException("Wrapped: " + string(e.what()));
}
};
try {
wrapException();
} catch (const DatabaseException& e) {
cout << "\nWrapped exception: " << e.what() << endl;
}
cout << endl;
// ========================================================
// DEMO 6: RAII and Exception Safety
// ========================================================
cout << "--- DEMO 6: RAII & EXCEPTION SAFETY ---" << endl << endl;
cout << "Without exception:" << endl;
{
SafeOperation op("resource1");
op.operate(false); // No throw
}
cout << "\nWith exception:" << endl;
try {
SafeOperation op("resource2");
op.operate(true); // Throws
} catch (const exception& e) {
cout << " Caught: " << e.what() << endl;
}
// Resource still properly released!
cout << "\n💡 Resources released even when exception thrown!" << endl;
cout << endl;
// ========================================================
// DEMO 7: noexcept Specification
// ========================================================
cout << "--- DEMO 7: NOEXCEPT ---" << endl << endl;
// noexcept means function won't throw
auto safeAdd = [](int a, int b) noexcept {
return a + b;
};
// noexcept(condition) - conditional
auto conditionalNoexcept = [](int x) noexcept(sizeof(int) == 4) {
return x * 2;
};
cout << "noexcept(safeAdd): " << noexcept(safeAdd(1, 2)) << endl;
cout << "noexcept(divide): " << noexcept(divide(1, 2)) << endl;
cout << "\nnoexcept benefits:" << endl;
cout << "• Enables optimizations" << endl;
cout << "• Required for move operations in containers" << endl;
cout << "• Documents function behavior" << endl;
cout << endl;
// ========================================================
// DEMO 8: Exception in Constructors/Destructors
// ========================================================
cout << "--- DEMO 8: EXCEPTIONS IN CTORS ---" << endl << endl;
class ThrowingClass {
private:
unique_ptr<Resource> r1;
unique_ptr<Resource> r2;
public:
ThrowingClass(bool throwInMiddle = false) {
r1 = make_unique<Resource>("first");
if (throwInMiddle) {
throw runtime_error("Construction failed!");
}
r2 = make_unique<Resource>("second");
}
};
cout << "Successful construction:" << endl;
{
ThrowingClass obj(false);
}
cout << "\nFailed construction:" << endl;
try {
ThrowingClass obj(true);
} catch (const exception& e) {
cout << " Caught: " << e.what() << endl;
}
// First resource properly cleaned up!
cout << endl;
// ========================================================
// EXCEPTION BEST PRACTICES
// ========================================================
cout << "--- EXCEPTION BEST PRACTICES ---" << endl << endl;
cout << "DO:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "✓ Throw by value, catch by const reference" << endl;
cout << "✓ Use standard exceptions when appropriate" << endl;
cout << "✓ Make destructors noexcept (implicit)" << endl;
cout << "✓ Use RAII for resource management" << endl;
cout << "✓ Catch at appropriate level" << endl;
cout << "✓ Provide informative error messages" << endl;
cout << "\nDON'T:" << endl;
cout << "─────────────────────────────────────────" << endl;
cout << "✗ Throw from destructors" << endl;
cout << "✗ Catch exceptions you can't handle" << endl;
cout << "✗ Use exceptions for normal flow control" << endl;
cout << "✗ Throw pointers (memory management issues)" << endl;
cout << "✗ Use catch(...) without rethrowing" << endl;
cout << endl;
cout << "============================================" << endl;
cout << "EXCEPTION HANDLING COMPLETE!" << endl;
cout << "============================================" << endl;
return 0;
}
// ============================================================
// EXERCISES:
// ============================================================
/*
* 1. Create a bank account class:
* - InsufficientFundsException
* - InvalidAmountException
* - AccountClosedException
* - Implement withdraw, deposit, transfer
*
* 2. Create a parser with custom exceptions:
* - SyntaxException with line number
* - SemanticException with context
* - Parse a simple config file format
*
* 3. Create exception-safe container:
* - SafeVector with bounds checking
* - Strong exception guarantee for push_back
* - Use smart pointers internally
*
* 4. Implement exception logging:
* - Log exception details to file
* - Include timestamp, type, message
* - Support for nested exceptions
*/