Concepts
In C++, an exception is an event that occurs during the execution of a program, which disrupts the normal flow of the program's instructions. Exceptions can be thrown by the program or by the runtime system, and they are used to handle error conditions or other exceptional situations.
When an exception is thrown, the program will stop executing its normal instructions and look for an exception handler that can handle the exception. An exception handler is a block of code that can catch and handle the exception, allowing the program to continue executing normally.
Implementation
Define an exception class and throw exception in program
The follow program illustrates how a custom exception class could be defined, and how exception could be called when a defined error happens.
#include <iostream>
#include <string>
using namespace std;
class MyException : public std::exception
{
public:
MyException( const std::string m)
: m_( m )
{}
virtual ~MyException() throw(){};
const char* what() const throw() {
return m_.c_str();
}
private:
std::string m_;
};
int main()
{
int a = 1, b = 0;
if (b==0){ // Define condition for MyException
string m ="Custom Exception. Divided by zero";
throw MyException(m); // Define the custom exception to be used
}
else cout<<"Result: "<<a/b; // If exception is called, this line is not run
return 0;
}
Output:
terminate called after throwing an instance of 'MyException'
what(): Custom Exception. Divided by zero
Notes:
- The custom exception class' destructor requires a
throw specifier
while that of the base classstd::exception
also does. If no custom destructor is defined, the complier creates a default destructor withoutthrow
specifier. - well-formed program requires that destructor in
MyException
is compatible with the destructor instd::exception
, that is:
virtual ~exception() throw(){}; // the destructor allows no exception
virtual ~MyException() throw(){}; // also allows no exeption
- otherwise,
~MyException()
would allow all exception and is incompatible with~exception() throw(){}
Reference:
1.https://stackoverflow.com/questions/18416780/overrriding-destructor-of-stdexception
Using try-catch to call exception in main program
The same program above could be rewritten using try-catch. Instead of using throw, try-catch is commonly used.
#include <iostream>
#include <string>
using namespace std;
class MyException : public std::exception
{
public:
MyException( const std::string m)
: m_( m )
{}
virtual ~MyException() throw(){};
const char* what() const throw() {
return m_.c_str();
}
private:
std::string m_;
};
int main()
{
try{
int a = 1000, b = 0;
if (b == 0){ // Define condition for MyException
string m = "Custom C++ Exception. Divided by zero";
throw MyException("Custom C++ Exception. Divided by zero"); // Define the custom exception to be used }
cout <<"result: "<<a/b<<endl;
return 0;
}
}
catch(MyException & e){
cout<<e.what();
return -1;
}
}
Output
Custom C++ Exception. Divided by zero
Define custom child exception class, inheriting a custom parent exception class:
Note: if multiple catch are used in main program then:
- If both base and derived classes are caught as exceptions, then the catch block of the derived class must appear before the base class.
- If we put the base class first then the derived class catch block will never be reached. For example, the following C++ code prints “Caught Base Exception“
Reference: https://www.geeksforgeeks.org/catching-base-and-derived-classes-as-exceptions-in-cpp-and-java/
The following programs shows that both MyException and MyExceptionChild are called, as show in the constructors output.
#include <iostream>
#include <string>
using namespace std;
class MyException : public std::exception
{
public:
MyException( const std::string m) : m_{ m }
{
cout<<"MyException"<<endl;
}
virtual ~MyException() throw(){};
const char* what() const throw() {
return m_.c_str();
}
private:
std::string m_;
};
class MyExceptionChild : public MyException
{
public:
MyExceptionChild( const std::string m) : MyException(m) // require :MyException(m)
{
cout<<"MyExceptionChild"<<endl;
childMsg_ = m;
}
virtual ~MyExceptionChild() throw(){};
const char* what() const throw() {
return childMsg_.c_str();
}
private:
std::string childMsg_;
};
int main()
{
try{
int a = 1000, b = 0;
if (b == 0){
throw MyExceptionChild("Custom C++ Exception. Divided by zero");
}
cout <<"result: "<<a/b<<endl;
}
catch(MyException & e){
cout<<e.what();
return -1;
}
catch(MyExceptionChild & e){
cout<<e.what();
return -1;
}
return 0;
}
Output:
main.cpp: In function ‘int main()’:
main.cpp:58:5: warning: exception of type ‘MyExceptionChild’ will be caught
58 | catch(MyExceptionChild & e){
| ^~~~~
main.cpp:53:5: warning: by earlier handler for ‘MyException’
53 | catch(MyException & e){
| ^~~~~
MyException
MyExceptionChild
Custom C++ Exception. Divided by zero
It is better to defined multiple catch blocks to specify the specific exceptions to be returned. In addition, it is better to place the catch block for child exception class as below:
What is the difference between throw() and throw
Throw
is defined as a C++ expression in https://en.cppreference.com/w/cpp/language/throw. Syntactically, it is followed by an exception class name. For example:
int a = 1, b = 0;
if (b==0){
string m ="Divided by zero";
throw MyException(m); //MyException is a class that inherit std::exception class
}
However, throw()
is used after a function such as:
void MyFunction(int i) throw();
or within a custom exception class, we also have:
class MyException : public std::exception
{
public:
MyException( const std::string m)
: m_( m )
{}
virtual ~MyException() throw(){};
const char* what() const throw() {
cout<<"MyException in ";
return m_.c_str();
}
private:
std::string m_;
};
`throw() is not an expression.It is the syntax to declare a non-throwing dynamic exception specification for a function, meaning it declares that the function does not throw any exceptions.
The syntax has been deprecated since C++11 and has finally completely been removed from the language with C++20. Therefore it shouldn't be used anymore. All uses of throw() on a function declaration in old code should be updated to noexcept instead.
References:
- non-throwing dynamic exception: https://en.cppreference.com/w/cpp/language/except_spec
- explaining throw() and throw: https://stackoverflow.com/questions/73551110/what-is-the-use-of-throw-after-a-function-declaration
How to re-throw exception
It is often that within the catch block that catches an exception, another function is called re-guide user to reinput data, correct the ealier error that has caused the exception. If the error is repeated, then re-throw exception is necessary.
The following program illustrates how to re-throw an exception. First the program as user to input denominator and nominator and calls devide
function. If the denominator is zero, it throws an exception and askes user to re-input data via another function call redoDivision
. The redoDivision
also throw the same exception if the denominator is zero again.
#include <iostream>
using namespace std;
class MyException : public std::exception
{
public:
MyException( const std::string m)
: m_( m )
{}
virtual ~MyException() throw(){};
const char* what() const throw() {
cout<<"location of exception object "<<this<<endl;
return m_.c_str();
}
private:
std::string m_;
};
void redoDivision(){
int denominator;
cout<<"reinput denominator: ";
cin>>denominator;
if (denominator == 0){
throw MyException("you put denominator =0 again !");
}
int nominator;
cout<<"reinput nominator: ";
cin>>nominator;
cout<<"Result: "<<nominator/denominator;
}
void divide(int denominator, int nominator){
try {
if (denominator == 0)
throw MyException("denominator value should not be 0\n ");
cout<<"Result: "<<nominator/denominator;
}
catch (MyException &e){
cout<<e.what();
redoDivision();
throw e;
}
}
int main()
{
int denominator, nominator;
cout<<"read denominator, nominator ";
cin>>denominator>>nominator;
divide(denominator, nominator);
return 0;
}
Output:
read denominator, nominator 0 1
location of exception object 0x562bb9e91750
denominator value should not be 0
reinput denominator: 0 1
terminate called after throwing an instance of 'MyException'
location of exception object 0x562bb9e91860
what(): you put denominator =0 again !
Note: 2 exception object were created.
- the "outer exception" was caught and handled. An object to the MyException was created and placed at location 0x562bb9e91750 and output the first error message denominator value should not be 0.
- the "inner exception" was caught by
redoDivision
. An object to the MyException was created and placed at location 0x562bb9e91860 and output the second error message you put denominator =0 again !.
Note: for every throw, there must by a try, catch. Or main
would catch the exception. The following program illustrates a wrong implementation that results in segmentation fault
.
#include <iostream>
#include <exception>
using namespace std;
class MyException : public exception {
public:
MyException(std::string msg) : message(msg) {};
private:
std::string message;
};
int main() {
int input;
cout << "Enter an integer: ";
cin >> input;
if (input == 0) {
throw MyException("Cannot input zero");
}
cout << "You entered: " << input << endl;
return 0;
}
If the program terminates due to the MyException
being thrown and not caught, the message that will be displayed on the console will be:
If the program terminates due to the MyException being thrown and not caught, the message that will be displayed on the console will be:
Aborted (core dumped)