How to Write Clean Exception Handling Code in C++

Exception handling is vital for producing code that functions properly under unusual conditions or, at a minimum, clearly explains errors to a user. This guide will introduce you to its principles in C++.

Written by Sadrach Pierre
Published on Jul. 07, 2022
A hand stops dominoes from falling
Brand Studio Logo

In computer programming, exception handling is the process of dealing with errors in a program such that it functions under unusual conditions. Further, it clearly explains why the program failed to the user. In many instances, a software program gets bad data or other unusual conditions that impact the code’s ability to run successfully. 

As a result, exception handling is key to software development because reliably handling errors can prevent software programs from exhibiting unintended behavior. Implementing error handling well ideally guarantees code will successfully run under any condition. 

Examples of unintended behavior include a program running slowly or failing upon receiving bad input. In the case of bad input, consider a function that takes a real-valued number (float) as user input, performs a calculation, and returns the result value. If the function expects a floating-point value but instead receives a string, the function will throw an error and fail. Error exception handling can be used to deal with cases like this. If a user inputs a bad value like a string, the program will return a meaningful message instead of failing or providing an unclear system error message. 

In C++, exception handling uses the expressions Try, Throw and Catch. The Try expression identifies the block of code that may have error exceptions. It may contain logic such as dividing two numbers or iterating over a list of numbers. The Throw expression handles the exception. For example, when your code tries to divide two numbers but the denominator is zero, you can throw an error message such as “Can’t divide by Zero.” Finally, the Catch expression handles the error. These expressions can handle a variety of exceptions in C++,  including length errors for arrays and vectors.

Here, we will walk through some simple examples of how to implement exception handling in C++. First, we’ll consider a runtime error example. We will define a function that takes height and weight as inputs and calculates body mass index. The formula for BMI is weight divided by height squared. If a value of zero is passed as an argument to the function, the code will attempt to divide by zero. We will use try, catch and throw to handle the runtime error. Finally, we will walk through an additional example of exception handling with length errors. Both of these examples should provide a good starting point for the developer just getting started with exception handling in C++. 

How to Write Clean Exception Handling Code in C++

In C++, exception handling uses the expressions Try, Throw and Catch. The Try expression identifies the block of code that may have error exceptions. It may contain logic such as dividing two numbers or iterating over a list of numbers. The Throw expression handles the exception. For example, when your code tries to divide two numbers but the denominator is zero, you can throw an error message such as “Can’t divide by Zero.” Finally, the Catch expression handles the error. These expressions can handle a variety of exceptions in C++,  including length errors for arrays and vectors.

More From Sadrach PierreHow to Create a List Array in Python


Runtime Exception Handling

To demonstrate exception handling for runtime error, we will walk through building a simple BMI calculator application. The app will take the user’s name, weight and height as inputs and display the calculated BMI. First, let’s walk through how to build out the application without any exception handling. 

To start, let's write a script that we will call bmi.cpp. We will include <iostream> and <string> headings. The <iostream>  heading allows us to write to the standard input/output streams. The <string> heading allows us to define string identifiers. 

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

When compiled and executed, the program will ask for the user’s name. To do this, we need to define a string variable for name and two float variables for weight and height, respectively:

// Main() function: where the execution of program begins
int main()
{
    string name;
    float weight;
    float height;
}

Next, let’s add the logic for asking for the user’s name:

// Main() function: where the execution of program begins
int main()
{
    string name;
    float weight;
    float height;
    cout << "Please Enter your Name \n";
    cin >> name;
}

 To compile our code, we run the following command in terminal:

g++  bmi.cpp

And we execute using the following command:

./a.out

We get the following output:

An output reading "Please enter your name."
Image: Screenshot by the author.

Now, let’s write logic that greets the user upon entering their name and asks for their weight:

// Main() function: where the execution of program begins
int main()
{
    string name;
    float weight;
    float height;
    cout << "Please Enter your Name \n";
    cin >> name;
    cout << "Hello " << name << ", please enter your weight in Kg \n";

}

If we compile and execute, we get the following:

An output reading "Hello Sadrach, please enter your weight in kg."
Image: Screenshot by the author.

We can then add the logic for weight as a user input:

int main()
{
    string name;
    float weight;
    float height;
    cout << "Please Enter your Name \n";
    cin >> name;
    cout << "Hello " << name << ", please enter your weight in Kg \n";
    cin >> weight;

}

The user will be prompted to enter a value for weight. Now, let’s include logic so that, upon providing a weight in kg, we ask the user for their height in meters:

int main()
{
    string name;
    float weight;
    float height;
    cout << "Please Enter your Name \n";
    cin >> name;
    cout << "Hello " << name << ", please enter your weight in Kg \n";
    cin >> weight;
    cout << "Thank you " << name << ", now please enter your height in meters \n";
    cin >> height;  
}
An output reading "Thank you Sadrach, now please enter your height in meters."
Image: Screenshot by the author.

Now we can use this data to calculate a BMI. The formula for BMI is weight/height2. I input the values Sadrach for my name, 90 kg for my weight and 1.92 m for my height:

int main()
{
    string name;
    float weight;
    float height;
    float bmi;
    cout << "Please Enter your Name \n";
    cin >> name;
    cout << "Hello " << name << ", please enter your weight in Kg \n";
    cin >> weight;
    cout << "Thank you " << name << ", now please enter your height in meters \n";
    cin >> height;  
    bmi = weight/(height*height);
    cout << "Your BMI is: " << bmi <<"\n";
}

With a height of 1.92 m and weight of 90 kg, we can calculate a BMI of 24.4:

An output reading "Your BMI is: 24.4141"
Image: Screenshot by the author.

Now, let’s try passing a value of zero for height:

An output reading "Your BMI is: inf"
Image: Screenshot by the author.

We see that, upon entering zero for height, we have a BMI calculation of “inf,” meaning infinity. This clearly isn’t a useful value for the user and may only cause confusion. Instead of displaying infinity, we should try to catch this error and display a message to the user saying that they provided an invalid value for height. We can do this by catching a runtime error.

First, at the top of our script, we need to import stdexcept to use runtime_error:

#include<stdexcept>

Next, we can rewrite the logic for our BMI calculation in a function:

float BMI_calculator(float weight, float height){
	if (height == 0){
	throw runtime_error("You attempted to calculate BMI with an invalid value of zero for height \n");
	}
	return weight/(height*height);
}

We can then modify our main function to try to calculate BMI:

 try{
        bmi = BMI_calculator(weight, height);
        cout << "Your BMI is: " << bmi <<"\n";
    }

 And catch the runtime error when it occurs. If it occurs, we display the text Warning: You attempted to calculate BMI with an invalid value of zero for height.

    catch (runtime_error&e){
        cout<< "Warning: "<< e.what();
    }

We then compile and execute our code, passing in zero as our input for height. We get the following display message:

An output that reads "Warning: You attempted to calculate BMI with an invalid value of zero for height"
Image: Screenshot by the author.

We see that this message is much more descriptive and useful than the original display message “Your BMI is: inf.” 

Build Your Engineering SkillsHow to Undo the Last Commit Using Git Reset Command


Length Error Exception Handling

Another common exception is the length error exception with vectors. To demonstrate the occurrence of this type of error, we will define a function that calculates the sum of monthly contributions to a retirement account. First, let’s define a C++ script called retirement_contributions.cpp.

Let’s add our main function as well as add the necessary <iostream> heading, a <vector> heading that will allow us to define vectors:

#include <iostream>
#include <stdexcept>  	// std::out_of_range
#include <vector>

using namespace std;
 
int main(){

}

Let’s add logic so that a user can specify the number of months over which they want to calculate the total retirement contribution:

#include <iostream>
#include <stdexcept>  	// std::out_of_range
#include <vector>
 
int main(){
	int months;
	cout << "Please enter the number of months \n";
	cin >> months;
}

Next, let’s define a vector that is the size of the number of months. The vector will contain dollar amount contributions, so it will be a vector of floats:

int main(){
	int months;
	cout << "Please enter the number of months \n";
	cin >> months;
     std::vector<float> contributions(months);

}

Let’s initialize a variable called current month, which will be the index we will use to iterate over our array:

int current_month = 1;

Let’s then define the first element of our array as our initial contribution:

contributions[1] = initial_contrbution;

And within a while loop, while the current month is less than the total number of months, we increase subsequent contributions by 2 percent:

while (current_month <= months){
    	    contributions[current_month + 1] 
=1.02*contributions[current_month];
	}

We can also display each month’s contribution while incrementally increasing our index by one:

while (current_month <= months){
    	    contributions[current_month + 1] 
=1.02*contributions[current_month];
    	    cout << "Month " << current_month << " contribution is: 
" << contributions[current_month]<< endl;
    	    current_month++;
	}

So, the full script is as follows:

int main(){
	int months;
	int current_month = 1;
	cout << "Please enter the number of months \n";
	cin >> months;
	std::vector<float> contributions(months);
	float initial_contrbution = 100;
	contributions[1] = initial_contrbution;
	while (current_month <= months){
    	    contributions[current_month + 1] =1.02*contributions[current_month];
    	    cout << "Month " << current_month << " contribution is: " << contributions[current_month]<< endl;
    	    current_month++;
	}
}

If we compile and execute this script using an input value of five months, we get the following output:

An output showing contributions over months
Image: Screenshot by the author.

From here, we can calculate the sum of contributions. Let’s initialize a float variable we will use to store the sum and, within the while loop, calculate it:

int main(){
	int months;
	int current_month = 1;
	cout << "Please enter the number of months \n";
	cin >> months;
     std::vector<float> contributions(months);
	float initial_contrbution = 100;
	float sum_contributions = 0;
	contributions[1] = initial_contrbution;
	while (current_month <= months){
    	    contributions[current_month + 1] =1.02*contributions[current_month];
    	    cout << "Month " << current_month << " contribution is: " << contributions[current_month]<< endl;
    	    sum_contributions += contributions[current_month];
    	current_month++;
	}
	    cout<<"Sum of contributions for " << months << " months is: "<<sum_contributions << endl;
}
An output that reads "Sum of contributions for 5 months is: 520.404
Image: Screenshot by the author.

Now let’s consider an exceptional case. Suppose a user accidentally inputs a negative value, let’s say negative five, for months. We get the following std::length_error:

An error message
Image: Screenshot by the author.

We see that our code fails with a cryptic message related to the length of our vector, std::length_error. This is because we can define a vector with a length of negative five. We can use exception handling to solve cases like this:

try{
   	    std::vector<float> contributions(months); //float contributions[months];
   	    float initial_contrbution = 100;
   	    float sum_contributions = 0;
   	    contributions[1] = initial_contrbution;
   	    while (current_month <= months){
    	        contributions[current_month + 1] =1.02*contributions[current_month];
    	        cout << "Month " << current_month << " contribution is: " << contributions[current_month]<< endl;
    	        sum_contributions += contributions[current_month];
    	        current_month++;
    }
   	cout<<"Sum of contributions for " << months << " months is: "<<sum_contributions << endl;
 
     catch (const std::length_error& le) {
      	    std::cerr << "Length of " << le.what() << " can't be negative \n";
	}

So, the full script is as follows:

int main(){
	int months;
	int current_month = 1;
	cout << "Please enter the number of months \n";
	cin >> months;
	try{
   	    std::vector<float> contributions(months); //float contributions[months];
   	    float initial_contrbution = 100;
   	    float sum_contributions = 0;
   	    contributions[1] = initial_contrbution;
   	    while (current_month <= months){
    	        contributions[current_month + 1] =1.02*contributions[current_month];
    	        cout << "Month " << current_month << " contribution is: " << contributions[current_month]<< endl;
    	        sum_contributions += contributions[current_month];
    	        current_month++;
    }
   	cout<<"Sum of contributions for " << months << " months is: "<<sum_contributions << endl;
 
     catch (const std::length_error& le) {
      	    std::cerr << "Length of " << le.what() << " can't be negative \n";
	}

	}
}

Now, if we compile, execute, and pass in a negative value for months, we appropriately handle the error:

An error message
Image: Screenshot by the author.

We see that by appropriately handling the length error, we’re able to display a descriptive message to the user. 

The scripts used in this blog are available on GitHub

More in Software EngineeringUseSelector and UseDispatch: A Guide to React-Redux Hooks

 

Wrapping Up Exception Handling

Exception handling is a very important part of software programming. It allows developers to handle unexpected behavior of code, anomalous inputs, unexpected runtimes, and much more. It is often a useful safeguard against code failing to run successfully.

Further, exceptional cases often result in cryptic or hard to understand system generated errors. Opaque, system-generated error messages can be frustrating to users, developers and engineers. If a user accidentally provides a bad input value, it is best to have measures for handling these types of values and providing a meaningful message to the user as to why the error occurred. Understanding exception handling is important for software developers, software engineers and even for machine learning engineers and data scientists. 

Explore Job Matches.