Exception handling is a crucial aspect of robust software development. It allows a program to deal with unexpected situations (exceptions) gracefully, ensuring that the program can continue to operate or terminate cleanly without crashing.

Key Concepts

  1. Exception: An event that disrupts the normal flow of the program's instructions.
  2. Try Block: A block of code that is tested for exceptions while it is being executed.
  3. Catch Block: A block of code that is executed if an exception occurs in the try block.
  4. Throw Statement: Used to signal the occurrence of an anomalous situation (exception).
  5. Exception Classes: Standard and user-defined classes that represent different types of exceptions.

Basic Syntax

Try, Catch, and Throw

try {
    // Code that may throw an exception
} catch (exception_type1 e1) {
    // Code to handle exception of type exception_type1
} catch (exception_type2 e2) {
    // Code to handle exception of type exception_type2
} // Additional catch blocks can be added as needed

Example

#include <iostream>
using namespace std;

int main() {
    int numerator, denominator;
    cout << "Enter numerator: ";
    cin >> numerator;
    cout << "Enter denominator: ";
    cin >> denominator;

    try {
        if (denominator == 0) {
            throw "Division by zero error!";
        }
        cout << "Result: " << numerator / denominator << endl;
    } catch (const char* msg) {
        cerr << "Error: " << msg << endl;
    }

    return 0;
}

Explanation

  • Try Block: Contains the code that might throw an exception.
  • Throw Statement: Throws an exception if the denominator is zero.
  • Catch Block: Catches the exception and handles it by printing an error message.

Standard Exception Classes

C++ provides several standard exception classes defined in the <stdexcept> header. Some commonly used ones include:

Exception Class Description
std::exception Base class for all standard exceptions
std::runtime_error Represents runtime errors
std::logic_error Represents logical errors
std::out_of_range Represents out-of-range errors
std::invalid_argument Represents invalid argument errors

Example Using Standard Exception Classes

#include <iostream>
#include <stdexcept>
using namespace std;

double divide(double numerator, double denominator) {
    if (denominator == 0) {
        throw runtime_error("Division by zero");
    }
    return numerator / denominator;
}

int main() {
    double num, denom;
    cout << "Enter numerator: ";
    cin >> num;
    cout << "Enter denominator: ";
    cin >> denom;

    try {
        double result = divide(num, denom);
        cout << "Result: " << result << endl;
    } catch (runtime_error& e) {
        cerr << "Error: " << e.what() << endl;
    }

    return 0;
}

Explanation

  • Throw Statement: Throws a runtime_error if the denominator is zero.
  • Catch Block: Catches the runtime_error and prints the error message using e.what().

Custom Exception Classes

You can define your own exception classes by inheriting from the std::exception class.

Example

#include <iostream>
#include <exception>
using namespace std;

class DivisionByZeroException : public exception {
public:
    const char* what() const noexcept override {
        return "Division by zero exception";
    }
};

double divide(double numerator, double denominator) {
    if (denominator == 0) {
        throw DivisionByZeroException();
    }
    return numerator / denominator;
}

int main() {
    double num, denom;
    cout << "Enter numerator: ";
    cin >> num;
    cout << "Enter denominator: ";
    cin >> denom;

    try {
        double result = divide(num, denom);
        cout << "Result: " << result << endl;
    } catch (DivisionByZeroException& e) {
        cerr << "Error: " << e.what() << endl;
    }

    return 0;
}

Explanation

  • Custom Exception Class: DivisionByZeroException inherits from std::exception and overrides the what() method.
  • Throw Statement: Throws a DivisionByZeroException if the denominator is zero.
  • Catch Block: Catches the DivisionByZeroException and prints the error message using e.what().

Practical Exercises

Exercise 1: Basic Exception Handling

Task: Write a program that takes two integers as input and performs division. Handle the case where the denominator is zero by throwing and catching an exception.

Solution:

#include <iostream>
using namespace std;

int main() {
    int numerator, denominator;
    cout << "Enter numerator: ";
    cin >> numerator;
    cout << "Enter denominator: ";
    cin >> denominator;

    try {
        if (denominator == 0) {
            throw "Division by zero error!";
        }
        cout << "Result: " << numerator / denominator << endl;
    } catch (const char* msg) {
        cerr << "Error: " << msg << endl;
    }

    return 0;
}

Exercise 2: Using Standard Exception Classes

Task: Modify the above program to use std::runtime_error for exception handling.

Solution:

#include <iostream>
#include <stdexcept>
using namespace std;

int main() {
    int numerator, denominator;
    cout << "Enter numerator: ";
    cin >> numerator;
    cout << "Enter denominator: ";
    cin >> denominator;

    try {
        if (denominator == 0) {
            throw runtime_error("Division by zero error!");
        }
        cout << "Result: " << numerator / denominator << endl;
    } catch (runtime_error& e) {
        cerr << "Error: " << e.what() << endl;
    }

    return 0;
}

Exercise 3: Custom Exception Class

Task: Create a custom exception class for division by zero and use it in your program.

Solution:

#include <iostream>
#include <exception>
using namespace std;

class DivisionByZeroException : public exception {
public:
    const char* what() const noexcept override {
        return "Division by zero exception";
    }
};

int main() {
    int numerator, denominator;
    cout << "Enter numerator: ";
    cin >> numerator;
    cout << "Enter denominator: ";
    cin >> denominator;

    try {
        if (denominator == 0) {
            throw DivisionByZeroException();
        }
        cout << "Result: " << numerator / denominator << endl;
    } catch (DivisionByZeroException& e) {
        cerr << "Error: " << e.what() << endl;
    }

    return 0;
}

Common Mistakes and Tips

  • Not Catching Exceptions: Always ensure that exceptions are caught to prevent the program from crashing.
  • Throwing Exceptions in Destructors: Avoid throwing exceptions in destructors as it can lead to undefined behavior.
  • Using Generic Catch Blocks: Be specific with catch blocks to handle different types of exceptions appropriately.

Conclusion

Exception handling is a powerful feature in C++ that helps in creating robust and error-resistant programs. By using try, catch, and throw statements, along with standard and custom exception classes, you can manage errors effectively and ensure your program behaves predictably even in unexpected situations.

© Copyright 2024. All rights reserved