codingstuff.io
ExploreTutorialsProblemsCS Subjects
Get Started
ExploreTutorialsProblemsCS Subjects
Get Started
codingstuff.io

Master the art of building software through interactive tutorials, real-world problems, and guided projects.

Pune, Maharashtra, India

codingstuffmail@gmail.com

Product

  • Explore
  • Tutorials
  • Problems
  • CS Subjects

Company

  • About
  • Contact
  • Privacy Policy
  • Terms & Conditions
  • Sitemap

© 2026 codingstuff.io. All rights reserved.

Built with ❤️ for developers everywhere

/
/
All Tutorials
⚡

C++ Programming

60 / 87 topics
55C++11 Features56Preprocessors and Macros57Templates (Function & Class Templates)58Namespaces59File Handling, Buffers, istream & ostream60Exception Handling, Asserts & Debugging61Multithreading
Tutorials/C++ Programming/Exception Handling, Asserts & Debugging
⚡C++ Programming

Exception Handling, Asserts & Debugging

Updated 2026-05-12
45 min read

Exception Handling, Asserts & Debugging

In this tutorial, we will explore advanced error handling techniques in C++ using try, catch, and throw. We'll dive into the exception hierarchy, learn how to create custom exceptions, and understand the importance of asserts for debugging. By the end of this lesson, you'll be equipped with robust tools to handle errors gracefully and ensure your programs run smoothly.

Introduction

Exception handling is a critical aspect of writing reliable software. It allows your program to respond appropriately when unexpected situations arise, preventing crashes and ensuring that resources are properly managed. In C++, exceptions provide a structured way to handle runtime errors, making your code more robust and maintainable.

In this tutorial, we'll cover the following topics:

  • Basic exception handling using try, catch, and throw.
  • The exception hierarchy in C++.
  • Creating custom exceptions.
  • Using asserts for debugging purposes.
  • Best practices for effective debugging.

Basic Exception Handling

Exception handling in C++ is based on three keywords: try, catch, and throw. Here's a simple example to illustrate how they work:

basic_exception.cpp
1#include <iostream>
2
3void divide(int numerator, int denominator) {
4 if (denominator == 0) {
5 throw "Division by zero error!";
6 }
7 std::cout << "Result: " << numerator / denominator << std::endl;
8}
9
10int main() {
11 try {
12 divide(10, 0);
13 } catch (const char* msg) {
14 std::cerr << "Error: " << msg << std::endl;
15 }
16 return 0;
17}
Output
Error: Division by zero error!

In this example:

  • The divide() function throws an exception if the denominator is zero.
  • The try block contains code that might throw an exception.
  • The catch block catches and handles the exception, printing an error message.

Exception Hierarchy

C++ provides a hierarchy of exceptions, all derived from the base class std::exception. This allows you to catch specific types of exceptions or handle them more generally. Here's a simple example demonstrating this:

exception_hierarchy.cpp
1#include <iostream>
2#include <stdexcept>
3
4void riskyFunction(int value) {
5 if (value < 0) {
6 throw std::invalid_argument("Negative value provided");
7 }
8 if (value > 100) {
9 throw std::out_of_range("Value out of range");
10 }
11}
12
13int main() {
14 try {
15 riskyFunction(200);
16 } catch (const std::out_of_range& e) {
17 std::cerr << "Out of range error: " << e.what() << std::endl;
18 } catch (const std::invalid_argument& e) {
19 std::cerr << "Invalid argument error: " << e.what() << std::endl;
20 } catch (const std::exception& e) {
21 std::cerr << "General exception: " << e.what() << std::endl;
22 }
23 return 0;
24}
Output
Out of range error: Value out of range

In this example:

  • std::invalid_argument and std::out_of_range are specific exception types.
  • The catch blocks catch these exceptions in a specific order, allowing you to handle different errors differently.

Creating Custom Exceptions

You can create your own custom exceptions by inheriting from std::exception. This allows you to provide more detailed error information and handle specific error cases in a more organized way. Here's an example:

custom_exception.cpp
1#include <iostream>
2#include <stdexcept>
3
4class MyException : public std::exception {
5public:
6 const char* what() const noexcept override {
7 return "My custom exception";
8 }
9};
10
11void riskyFunction(int value) {
12 if (value == 0) {
13 throw MyException();
14 }
15}
16
17int main() {
18 try {
19 riskyFunction(0);
20 } catch (const MyException& e) {
21 std::cerr << "Caught my custom exception: " << e.what() << std::endl;
22 } catch (const std::exception& e) {
23 std::cerr << "General exception: " << e.what() << std::endl;
24 }
25 return 0;
26}
Output
Caught my custom exception: My custom exception

In this example:

  • MyException is a custom exception class derived from std::exception.
  • The what() method returns a descriptive error message.

Asserts for Debugging

Asserts are a powerful tool for debugging. They allow you to verify assumptions in your code and ensure that certain conditions are met. C++ provides two types of asserts: runtime asserts (assert()) and compile-time asserts (static_assert).

Runtime Asserts with assert()

Runtime asserts check conditions at runtime and terminate the program if the condition is false. Here's an example:

runtime_assert.cpp
1#include <iostream>
2#include <cassert>
3
4void process(int value) {
5 assert(value > 0 && "Value must be positive");
6 std::cout << "Processing value: " << value << std::endl;
7}
8
9int main() {
10 process(-5);
11 return 0;
12}
Output
Assertion failed: Value must be positive, file runtime_assert.cpp, line 7

In this example:

  • The assert() macro checks if the condition is true.
  • If the condition is false, the program terminates and displays an error message.

Compile-Time Asserts with static_assert

Compile-time asserts check conditions at compile time. They are useful for validating template parameters or other constants. Here's an example:

compile_time_assert.cpp
1#include <iostream>
2
3template<typename T>
4void process(T value) {
5 static_assert(std::is_integral<T>::value, "T must be an integral type");
6 std::cout << "Processing value: " << value << std::endl;
7}
8
9int main() {
10 process(10);
11 // process(3.14); // Uncommenting this line will cause a compile-time error
12 return 0;
13}
Output
compile_time_assert.cpp: In instantiation of 'void process(T) [with T = double]': compile_time_assert.cpp:12:9: required from here compile_time_assert.cpp:6:5: error: static assertion failed: T must be an integral type

In this example:

  • static_assert() checks if the condition is true at compile time.
  • If the condition is false, a compile-time error is generated.

Best Practices for Debugging

  1. Use Specific Exception Types: Catch specific exceptions rather than general ones to handle errors more precisely.
  2. Provide Detailed Error Messages: Use descriptive messages in your what() methods and assert statements to make debugging easier.
  3. Enable Assertions in Debug Builds: Compile with assertions enabled (-DDEBUG) to catch potential issues early.
  4. Use Logging: Implement logging to capture detailed information about program execution, especially in complex applications.

Practical Example

Let's put everything together in a practical example. We'll create a simple calculator that performs basic arithmetic operations and handles errors gracefully.

calculator.cpp
1#include <iostream>
2#include <stdexcept>
3
4class DivisionByZeroException : public std::exception {
5public:
6 const char* what() const noexcept override {
7 return "Division by zero error";
8 }
9};
10
11double divide(double numerator, double denominator) {
12 if (denominator == 0) {
13 throw DivisionByZeroException();
14 }
15 return numerator / denominator;
16}
17
18int main() {
19 try {
20 std::cout << "Enter two numbers: ";
21 double a, b;
22 std::cin >> a >> b;
23
24 std::cout << "Result of division: " << divide(a, b) << std::endl;
25 } catch (const DivisionByZeroException& e) {
26 std::cerr << "Error: " << e.what() << std::endl;
27 } catch (const std::exception& e) {
28 std::cerr << "General error: " << e.what() << std::endl;
29 }
30 return 0;
31}

In this example:

  • We define a custom exception DivisionByZeroException for division by zero errors.
  • The divide() function throws this exception if the denominator is zero.
  • The main() function handles these exceptions and provides appropriate error messages.

Summary

In this tutorial, you learned about advanced C++ concepts related to exception handling, asserts, and debugging. Key takeaways include:

  • Using try, catch, and throw for basic exception handling.
  • Understanding the exception hierarchy and catching specific exceptions.
  • Creating custom exceptions for more detailed error handling.
  • Utilizing runtime (assert()) and compile-time (static_assert) asserts for debugging.
  • Best practices for effective debugging, including using specific exception types and providing detailed error messages.

What's Next?

Now that you have a solid understanding of exception handling and debugging in C++, the next topic is Multithreading. Multithreading allows your programs to perform multiple tasks concurrently, improving performance and responsiveness. In the next lesson, we'll explore how to create and manage threads in C++ using the &lt;thread&gt; library.

Stay tuned for more advanced topics in C++ programming!


PreviousFile Handling, Buffers, istream & ostreamNext Multithreading

Recommended Gear

File Handling, Buffers, istream & ostreamMultithreading