Search

Enter your search terms

Submit search form

14.2 — Basic exception handling

By Alex on October 4th, 2008 | last modified by Alex on February 1st, 2018

In the previous lesson on the need for exceptions, we talked about how using return codes causes your control flow and error flow to be intermingled, constraining both. Exceptions in C++ are implemented using three keywords that work in conjunction with each other: throw, try, and catch.

Throwing exceptions

We use signals all the time in real life to note that particular events have occurred. For example, during American football, if a player has committed a foul, the referee will throw a flag on the ground and whistle the play dead. A penalty is then assessed and executed. Once the penalty has been taken care of, play generally resumes as normal.

In C++, a throw statement is used to signal that an exception or error case has occurred (think of throwing a penalty flag). Signaling that an exception has occurred is also commonly called raising an exception.

To use a throw statement, simply use the throw keyword, followed by a value of any data type you wish to use to signal that an error has occurred. Typically, this value will be an error code, a description of the problem, or a custom exception class.

Each of these statements acts as a signal that some kind of problem that needs to be handled has occurred.

Looking for exceptions

Throwing exceptions is only one part of the exception handling process. Let’s go back to our American football analogy: once a referee has thrown a penalty flag, what happens next? The players notice that a penalty has occurred and stop play. The normal flow of the football game is disrupted.

In C++, we use the try keyword to define a block of statements (called a try block). The try block acts as an observer, looking for any exceptions that are thrown by any of the statements within the try block.

Here’s an example of a try block:

1

2

3

4

5

try

{

// Statements that may throw exceptions you want to handle go here

throw-1;// here's a trivial throw statement

}

Note that the try block doesn’t define HOW we’re going to handle the exception. It merely tells the program, “Hey, if any of the statements inside this try block throws an exception, grab it!”.

Handling exceptions

Finally, the end of our American football analogy: After the penalty has been called and play has stopped, the referee assesses the penalty and executes it. In other words, the penalty must be handled before normal play can resume.

Actually handling exceptions is the job of the catch block(s). The catch keyword is used to define a block of code (called a catch block) that handles exceptions for a single data type.

Here’s an example of a catch block that will catch integer exceptions:

1

2

3

4

5

catch(intx)

{

// Handle an exception of type int here

std::cerr<<"We caught an int exception with value"<<x<<std::endl;

}

Try blocks and catch blocks work together -- A try block detects any exceptions that are thrown by statements within the try block, and routes them to the appropriate catch block for handling. A try block must have at least one catch block immediately following it, but may have multiple catch blocks listed in sequence.

Once an exception has been caught by the try block and routed to a catch block for handling, the exception is considered handled, and execution will resume as normal after the catch block.

Catch parameters work just like function parameters, with the parameter being available within the subsequent catch block. Exceptions of fundamental types can be caught by value, but exceptions of non-fundamental types should be caught by const reference to avoid making an unnecessary copy.

Just like with functions, if the parameter is not going to be used in the catch block, the variable name can be omitted:

1

2

3

4

5

catch(double)// note: no variable name since we don't use it in the catch block below

{

// Handle exception of type double here

std::cerr<<"We caught an exception of type double"<<std::endl

}

This can help prevent compiler warnings about unused variables.

Putting throw, try, and catch together

Here’s a full program that uses throw, try, and multiple catch blocks:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

#include <iostream>

#include <string>

intmain()

{

try

{

// Statements that may throw exceptions you want to handle go here

throw-1;// here's a trivial example

}

catch(intx)

{

// Any exceptions of type int thrown within the above try block get sent here

std::cerr<<"We caught an int exception with value: "<<x<<std::endl;

}

catch(double)// no variable name since we don't use the exception itself in the catch block below

{

// Any exceptions of type double thrown within the above try block get sent here

std::cerr<<"We caught an exception of type double"<<std::endl;

}

catch(conststd::string&str)// catch classes by const reference

{

// Any exceptions of type std::string thrown within the above try block get sent here

std::cerr<<"We caught an exception of type std::string"<<std::endl;

}

std::cout<<"Continuing on our merry way\n";

return0;

}

Running the above try/catch block would produce the following result:

We caught an int exception with value -1
Continuing on our merry way

A throw statement was used to raise an exception with the value -1, which is of type int. The throw statement was then caught by the enclosing try block, and routed to the appropriate catch block that handles exceptions of type int. This catch block printed the appropriate error message.

Once the exception was handled, the program continued as normal after the catch blocks, printing “Continuing on our merry way”.

Recapping exception handling

Exception handling is actually quite simple, and the following two paragraphs cover most of what you need to remember about it:

When an exception is raised (using throw), execution of the program immediately jumps to the nearest enclosing try block (propagating up the stack if necessary to find an enclosing try block -- we’ll discuss this in more detail next lesson). If any of the catch handlers attached to the try block handle that type of exception, that handler is executed and the exception is considered handled.

If no appropriate catch handlers exist, execution of the program propagates to the next enclosing try block. If no appropriate catch handlers can be found before the end of the program, the program will fail with an exception error.

Note that the compiler will not perform implicit conversions or promotions when matching exceptions with catch blocks! For example, a char exception will not match with an int catch block. An int exception will not match a float catch block.

That’s really all there is to it. The rest of this chapter will be dedicated to showing examples of these principles at work.

Exceptions are handled immediately

Here’s a short program that demonstrates how exceptions are handled immediately:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

#include <iostream>

intmain()

{

try

{

throw4.5;// throw exception of type double

std::cout<<"This never prints\n";

}

catch(doublex)// handle exception of type double

{

std::cerr<<"We caught a double of value: "<<x<<'\n'<<std::endl;

}

return0;

}

This program is about as simple as it gets. Here’s what happens: the throw statement is the first statement that gets executed -- this causes an exception of type double to be raised. Execution immediately moves to the nearest enclosing try block, which is the only try block in this program. The catch handlers are then checked to see if any handler matches. Our exception is of type double, so we’re looking for a catch handler of type double. We have one, so it executes.

Consequently, the result of this program is as follows:

We caught a double of value: 4.5

Note that “This never prints” is never printed, because the exception caused the execution path to jump immediately to the exception handler for doubles.

A more realistic example

Let’s take a look at an example that’s not quite so academic:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

#include "math.h" // for sqrt() function

#include <iostream>

intmain()

{

std::cout<<"Enter a number: ";

doublex;

std::cin>>x;

try// Look for exceptions that occur within try block and route to attached catch block(s)

{

// If the user entered a negative number, this is an error condition

if(x<0.0)

throw"Can not take sqrt of negative number";// throw exception of type const char*

// Otherwise, print the answer

std::cout<<"The sqrt of "<<x<<" is "<<sqrt(x)<<'\n';

}

catch(constchar*exception)// catch exceptions of type const char*

{

std::cerr<<"Error: "<<exception<<std::endl;

}

}

In this code, the user is asked to enter a number. If they enter a positive number, the if statement does not execute, no exception is thrown, and the square root of the number is printed. Because no exception is thrown in this case, the code inside the catch block never executes. The result is something like this:

Enter a number: 9
The sqrt of 9 is 3

If the user enters a negative number, we throw an exception of type const char*. Because we’re within a try block and a matching exception handler is found, control immediately transfers to the const char* exception handler. The result is:

Enter a number: -4
Error: Can not take sqrt of negative number

By now, you should be getting the basic idea behind exceptions. In the next lesson, we’ll do quite a few more examples to show how flexible exceptions are.

What catch blocks typically do

If an exception is routed to a catch block, it is considered “handled” even if the catch block is empty. However, typically you’ll want your catch blocks to do something useful. There are three common things that catch blocks do when they catch an exception:

First, catch blocks may print an error (either to the console, or a log file).

Second, catch blocks may return a value or error code back to the caller.

Third, a catch block may throw another exception. Because the catch block is outside of the try block, the newly thrown exception in this case is not handled by the preceding try block -- it’s handled by the next enclosing try block.

"Third, a catch block may throw another exception. Because the catch block is outside of the try block, the newly thrown exception in this case is not handled by the preceding try block -- it’s handled by the next enclosing try block."
Taking into consideration your this point, I tried to Do Something Like This :-

Just some small problems. In "Putting throw, try, and catch together" section, there are 2 instances of "cerr" in the code, which should be "std::cerr". Also a space is missing between the string and x.

Hey Alex, I found a way to check if the user inputs a letter or string instead of a number. I made the user input a string and then converted it to a double using std::stod. Then I found out on the c++ website that if std::stod fails, it throws an object of std::invalid_argument (must include <stdexcept>). So I catch that exception and put out my own statement. Is there anything wrong with this approach?
Here is my code.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

#include <iostream>

#include <cmath>

#include <string>

#include <stdexcept>

intmain(intargc,char*argv[])

{

std::cout<<"Enter a number: ";

std::stringx;

std::cin>>x;

try

{

if(std::stod(x)<0.0)

throw"Can not take sqrt of negative number";

// Otherwise, print the answer

std::cout<<"The sqrt of "<<x<<" is "<<sqrt(std::stod(x))<<'n';

}

catch(constchar*exception)// catch exceptions of type const char*

{

std::cerr<<"Error: "<<exception<<std::endl;

}

catch(conststd::invalid_argument&ia)

{

std::cerr<<"Invalid input. Numbers only!"<<std::endl;

}

std::cin.clear();

std::cin.ignore(32767,'n');

std::cin.get();

return0;

}

I found 1 issue actually. If the input starts with a number and then there are letters, it works. So if i write 9asefdas for input, the output will be 3. It ignores the rest for some reason. How do i fix that?
and Happy New Year!

std::stod() can take an optional second parameter: double stod (const string& str, size_t* idx = 0); See this page for more info. You can test whether the return value of this second parameter is equal to the strlen() of the input string. If so, then stod() parsed the entire string. If not, then there must be non-numeric values it couldn't parse.

I believe you've used the phrase "attached catch block" a few times within this chapter, but I am still unsure exactly what "attached" means in this context. Does the catch block(s) literally have to come directly after the try block, or can there be code in between?

In the example in section "A more realistic example" - line 19:
Using GCC 4.8.1 (-std=c++11) to compile the program and entering a negative number causes the program to crash with the following message:
[code]
terminate called after throwing an instance of 'char const*'

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
[code]
However using Visual Studio doesn't have this problem. My question is "some_string" is a literal constant of type <const char*> but how come the Visual Studio compiler is able to handle the exception of type <const char*> with a catch block of type <char*> ? Are the catch blocks strongly typed (there are no implicit type casting) or not ?

/*program to show the concept of exception or error handling*/
#include
#include
#include
void main()
{
int count=1,size,a=0; //variable count and a are declared and initialized with count=1 and a=0
cout<>size; //size of loop entered
ankur: //jump statement body starts
try //try body starts
{
a=a+1; //value of a is incremented by 1 everytime when try body executes
//cout<<a<<endl;
if (a%2==0) //condition starts if a is even then throw for even will executes otherwise throw of odd is executed
{ //in this no need to write the else statement because it's not required here
throw ("Error! it's even");
}
//cout<<"it's odd\n";
throw("Error! it's odd"); //automatically if IF condition went wrong then this throw will exucute
}
catch(char * ptr) //this keyword will work only with the help of char pointer variable and holds the string value of throw
{
cout<<count<<". "<<ptr<<endl;;
count=count+1;
if (count<=size)
{
goto ankur; //if count becomes greator than size then the condition will terminate and main will end here
}
else
{
exit(0);
}
}
getch();
}

In the last example, the try block purposefully doesn’t throw anything, because it doesn’t really have any context to determine that something went wrong. Instead, it’s leaving it up to ReadParameter() to throw an exception if something went wrong. If ReadParameter() throws an int exception, the catch block will catch it and handle it.