PDFVersion
No ads? No problem! Download the PDF book of this tutorial for just $24.99. Your support will help us reach more readers.
Thank You!
EXCEPTION HANDLING:
Occasionally, functions don't perform as expected, even ones we've written ourselves. Many novice programmers assume their code will always run smoothly, but seasoned developers understand that unexpected issues can arise. For instance, when attempting to read a file from disk, it may not exist, or when writing to a disk, it could be full or unformatted. Similarly, if a program prompts user input, users might provide invalid data.
These errors that occur during the execution of Object-Oriented Programming are termed "Exceptions," and the techniques used to manage them fall under the umbrella of Exception Handling.
C++ Exception handling relies on three keywords: try
, catch
, and throw
. The fundamental idea is to encapsulate code that might encounter issues within a try block. If an exception arises, the throw keyword is used to signal it, and then one or more catch blocks can handle the exception appropriately. A simple illustration of this concept is a program that divides two numbers:
#include<iostream>
using namespace std;
int divide(int a, int b) {
return a / b;
}
int main() {
int x, y;
cout << "Enter two numbers: " << endl;
cin >> x >> y;
cout << divide(x, y);
return 0;
}
This code works fine under normal circumstances. However, what if the user inputs "0" as the divisor? Since dividing by zero is impossible, the program would likely encounter a runtime error or exception. In a real-world scenario, such errors could confuse users and developers alike. To address this, we can employ try, catch, and throw.
A try
block identifies a section of code where specific exceptions might occur. It's followed by one or more catch
blocks. The code within the try block is what we want to monitor for errors. This section can range from a few statements within a function to encapsulating the entire main()
function, effectively monitoring the entire program.
When a problem arises during execution, a program throws an exception using the throw
keyword. The general form is throw exception;. If this exception needs to be caught, it must be executed within a try
block or any function called from within the try
block.
An exception is caught by a program using an exception handler (catch
block) where we want to manage the problem. The catch
keyword signifies catching an exception. A try
block can have multiple catch statements associated with it. The type of the exception determines which catch
block is utilized.
If an exception is thrown but no applicable catch statement exists, the program will terminate abnormally. Hence, it's crucial to catch all potential exceptions.
The syntax for try
and catch
blocks is as follows:
try
{
// Code inside the try block
throw exception;
}
catch(type exception)
{
// Code to handle the exception
}
The code within the try block executes normally. If an exception occurs, the throw keyword is used within the try block to raise it. The catch block then handles the exception, receiving the exception passed by throw.
Consider this example:
#include<iostream>
using namespace std;
int main()
{
int x;
cout << "Enter the value of x: ";
cin >> x;
cout << "Before try: " << endl;
try
{
cout << "Inside try: " << endl;
if(x < 0)
{
throw x;
}
}
catch (int x)
{
cout << "Exception Caught: " << endl;
}
cout << "After catch: " << endl;
return 0;
}
C++ provides a set of standard exceptions defined in <exception>
that we can use in our programs. Here's an overview of these exceptions:
EXCEPTION | DESCRIPTION |
---|---|
bad_alloc: | Indicates failure to allocate storage during dynamic memory allocation via the new operator. |
bad_cast: | Thrown when a dynamic_cast1to a reference type fails runtime check due to unrelated types. |
bad_typeid: | Thrown when applying the typeid2operator to a dereferenced null pointer of a polymorphic type. |
bad_exception: | Represents unexpected exceptions in C++ programs, thrown by the runtime. |
logic_error: | Reports errors from faulty logic within the program, such as violating logical preconditions. |
domain_error: | Indicates errors resulting from inputs outside the defined domain or operations. |
invalid_argument: | Reports errors due to unacceptable argument values. |
length_error: | Signifies attempts to exceed length limits defined for an object. |
out_of_range: | Indicates attempts to access elements outside a defined range, like with the at method. |
runtime_error: | Reports errors beyond the program's scope that cannot be predicted easily. |
overflow_error: | Used to report arithmetic overflow errors where the result is too large. |
range_error: | Signals errors where the result of a computation cannot be represented by the destination type. |
underflow_error: | Reports arithmetic underflow errors where the result is a subnormal floating-point value. |
1 - Dynamic_cast:
It's an operator used to safely convert from a pointer or reference to a base type to a pointer or reference to a derived type.
2 - typeid Operator:
This operator returns the actual type of the object referred to by a pointer or a reference.
We can define custom exceptions by inheriting and overriding functionality from the exception
class. Below is an example:
#include <iostream>
#include <exception>
using namespace std;
struct MyException : public exception
{
const char * what () const throw ()
{
return "C++ Exception";
}
};
int main()
{
try
{
throw MyException();
}
catch(MyException& e)
{
cout << "MyException caught" << endl;
cout << e.what() << endl;
}
catch(exception& e)
{
// Other errors
}
return 0;
}
Here, what()
is a public method provided by the exception
class and has been overridden by all child exception classes to return the cause of an exception.
Sardar Omar
I did my hardest to present you with all of the information you need on this subject in a simple and understandable manner. However, if you have any difficulties understanding this concept or have any questions, please do not hesitate to ask. I'll try my best to meet your requirements.