OOP Unit 5 Notes
OOP Unit 5 Notes
Unit –V
Exception Handling and Templates
1. Exception Handling
Exception handling is a mechanism used in C++ to deal with runtime errors (exceptions) that
may occur during program execution. Without exception handling, when an error occurs, the
program terminates abruptly, which can lead to data loss or an inconsistent state. Exception
handling helps manage such errors by allowing the program to handle them gracefully and
recover without terminating immediately.
C++ exceptions are triggered when an error or unexpected condition occurs during program
execution. Below are four common examples of exceptions:
1. Division by Zero
Cause: Happens when a program tries to access an array index that is outside its valid
range.
Cause: Occurs when a program tries to open or read a file that does not exist or is
inaccessible.
2|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
Explanation: The file handling functions fail, and the program cannot proceed.
Handling: Check if the file exists before accessing it, or use exception handling to
manage the error.
Cause: Happens when a program attempts to convert between incompatible types, such
as converting a string to an integer.
For example:
Without exception handling, errors cause the program to terminate, potentially leading to:
Financial loss: For programs involving transactions or sensitive data, an error can cause
significant problems.
1. Remedial Action: The program tries to fix the issue and continues execution.
2. Retry: The program displays the error and prompts the user to correct the mistake.
2. catch: Catches and handles the exception thrown by the try block.
Structure of try-catch
try {
// Code that may generate an exception
}
catch (Type variable) {
// Code to handle the exception
}
The try block contains code that may throw an exception.
The catch block catches the thrown exception and handles it.
Type is the data type of the exception being thrown (e.g., int, string).
Key Points:
A program can have multiple catch blocks to handle different types of exceptions.
If an exception is thrown, the control jumps directly to the catch block, and the rest of
the try block is skipped.
The data type of the thrown exception must match the type in the catch block to ensure
proper handling.
#include <iostream>
using namespace std;
private:
const char* errorMessage;
};
int main() {
try {
// Simulate an error by throwing a custom exception
throw MyException("Custom exception occurred!");
} catch (MyException& e) {
// Catch the custom exception and display the error message
cout << "Caught exception: " << e.getErrorMessage() << endl;
}
return 0;
}
1.2 Simple Exception Handling - Divide by Zero
#include <iostream>
using namespace std;
if (b == 0) {
throw "Division by zero error"; // Throw an exception if
// divisor is zero
}
return a / b;
}
int main() {
int numerator, denominator;
try {
int result = divide(numerator, denominator);
// Attempt division
cout << "Result: " << result << endl;
}
catch (const char* msg) { // Catch and handle the exception
cout << "Exception caught: " << msg << endl;
}
return 0;
}
Sample Output: Case 2: Division by zero
In C++, exception handling is managed using the keywords try, catch, and throw. This
allows a program to detect and handle errors (exceptions) gracefully during execution.
try block: This contains the code that might throw an exception.
catch block: This handles the exception. Multiple catch blocks can be used to handle
different types of exceptions separately.
throw keyword: This is used to throw an exception from the try block.
When an exception occurs in the try block, the control is transferred to the corresponding catch
block that matches the type of the thrown exception. If no exception occurs, none of the catch
blocks are executed. Multiple catch blocks allow the programmer to handle different types of
exceptions separately, based on the type of exception thrown.
Each catch block handles a specific type of exception, and only the first matching catch block
is executed. After the first matching block executes, the remaining blocks are skipped.
try {
// Code that may generate exceptions
}
catch (Type1 variable) {
// Code to handle exception of type Type1
}
catch (Type2 variable) {
// Code to handle exception of type Type2
}
catch (Type_n variable) {
// Code to handle exception of type Type_n
}
Example: Consider a program where we perform division and subtraction, and exceptions are
thrown in specific conditions.
7|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
#include <iostream>
using namespace std;
int main() {
int e1, sub;
float a, b, div, e;
try {
cout << "Enter two numbers: ";
cin >> a >> b;
div = a / b;
cout << "\nDivision is: " << div;
sub = a - b;
cout << "\nSubtraction is: " << sub;
cout << "\nTry block ends here" << endl;
}
catch (float e) {
cout << "\nException - Division by zero" << endl;
}
catch (int e1) {
cout << "\nException - First number smaller than second" <<
endl;
}
8|Page © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
Case 1: Both numbers are valid (second is not zero, and first is not smaller than the
second), so the program executes normally without exceptions.
Case 2: The second number is zero, causing a division by zero exception (float type).
The corresponding catch block for the float exception handles it.
Case 3: The first number is smaller than the second, causing an int exception (e1). The
corresponding catch block for the int type handles it.
In C++, exception handling is commonly done using the try, catch, and throw keywords.
Typically, specific exceptions are handled by defining multiple catch blocks, each handling a
different type of exception. However, there might be situations where you want to handle all
types of exceptions with a single catch block, without needing to define a separate handler for
each type.
To achieve this, C++ provides a special type of catch block that handles all exceptions,
regardless of their type. This is done using catch(...).
try {
// Code that may throw exceptions
}
catch(...) {
// Code to handle any type of exception
}
The three dots (...) in the catch block indicate that it is capable of catching all types of
exceptions. This block acts as a general error handler when you are unsure of the specific types
of exceptions that might be thrown or if you want to handle all exceptions in the same way.
You don’t know all the possible types of exceptions that could be thrown in your
program.
You don’t want to write separate catch blocks for each type of exception.
However, it is important to note that the catch(...) block should be placed last after all
specific catch blocks, as C++ will try to match exceptions with the first available catch block.
If catch(...) appears before specific handlers, it will catch all exceptions, preventing any
other handlers from being executed.
Consider the following example where we use a catch-all block to handle different types of
exceptions:
#include <iostream>
using namespace std;
int main() {
int e1, sub;
float a, b, div, e;
try {
cout << "Enter two numbers: ";
cin >> a >> b;
div = a / b;
cout << "\nDivision is: " << div;
10 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
sub = a - b;
cout << "\nSubtraction is: " << sub;
cout << "\nTry block ends here" << endl;
}
catch(...) {
cout << "\nException raised" << endl;
}
Case 1: If both numbers are valid (second is not zero, and the first number is not greater
than the second), the program executes normally without any exceptions.
Case 2: If the second number is zero, a division by zero exception is thrown. Since we
use a catch(...) block, it handles the exception and prints "Exception raised".
Case 3: If the first number is greater than the second, an exception (e1) is thrown.
Again, the catch(...) block handles this exception, printing "Exception raised".
In C++, an exception can be rethrown from within a catch block using the throw statement
without any arguments. This is useful for propagating the exception to an outer try-catch block,
allowing the same exception to be handled differently by multiple handlers.
Key Points
throw; inside a catch block rethrows the caught exception to the next matching
handler in an outer try-catch block.
11 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
Useful when an inner handler performs partial handling and delegates further
processing to an outer handler.
Example
#include <iostream>
using namespace std;
int main() {
int a;
try { // Outer try block
try { // Inner try block
cout << "Enter a number: ";
cin >> a;
if (a == 1)
throw 5; // Throw an integer exception
cout << "Try ends, a = " << a << endl;
}
catch (int ex) { // Inner catch block
cout << "Inner catch, exception = " << ex << endl;
throw; // Rethrow the exception
}
}
catch (int ex) { // Outer catch block
cout << "Outer catch, exception = " << ex << endl;
}
cout << "Program ends\n";
return 0;
}
Explanation
Case 1: If a == 1:
12 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
The inner catch block handles it partially and rethrows it using throw;.
Case 2: If a != 1:
No exception is thrown, so the inner block executes successfully, and no catch blocks
are triggered.
Exception specification in C++ is a mechanism that limits the types of exceptions a function
can throw. It serves as a guarantee to the caller of the function, specifying which exceptions
might be thrown.
Syntax
type1, type2, ...: List of exception types the function is allowed to throw.
throw(): Indicates that the function will not throw any exceptions.
Key Points
1. If a function throws an exception that is not in its exception specification, the runtime
calls unexpected().
2. A function with no exception specification (throw()) guarantees it will not throw any
exceptions.
3. A function without any exception specification can throw exceptions of any type.
4. Exception specification is enforced only at runtime, meaning the compiler does not
prevent exceptions outside the specified list.
If a function calls another function with a broader exception specification (i.e., one that
allows more exception types), any exceptions not in the outer function's specification
must be handled by the outer function.
13 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
Example
#include <iostream>
using namespace std;
int main() {
try {
outerFunction();
} catch (...) {
cout << "Unhandled exception!" << endl;
}
return 0;
}
Explanation
1. The subFunction and outerFunction both specify that they can throw int
exceptions.
In C++, we can create custom exception classes to handle specific error conditions in a
program. These are called user-defined exceptions and are typically derived from the standard
exception class.
Key Features
1. Custom Class: Define a class for the exception, usually derived from the built-in
exception class.
2. Members: The class can include data members, constructors, and member functions
for custom error handling or displaying error messages.
4. Catching the Exception: To handle the exception, define a catch block that matches
the type of the user-defined class.
Example: The following program throws and handles a user-defined exception if the second
number is smaller than the first:
#include <iostream>
using namespace std;
int main() {
15 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
int a, b;
cout << "Enter two numbers: ";
cin >> a >> b;
try {
if (a < b) {
// Create and throw user-defined exception object
DemoExpt obj;
throw obj;
} else {
cout << "Ans = " << (a - b) << endl;
}
} catch (DemoExpt ex) { // Handle user-defined exception
ex.showErr();
}
2. Throwing an Exception:
o The catch block matches the DemoExpt type and calls the showErr function to display
the error message.
In C++, exceptions are usually handled using a try-catch block. However, if an exception is
thrown and no matching catch block is found, the exception handling system performs the
following actions:
1. Call to unexpected(): If no catch block matches the exception type, the system calls
the unexpected() function. By default, this function calls terminate().
You can change the default termination behavior by using the set_terminate() function,
defined in the <exception> header. This function allows you to set a custom termination
handler that will execute instead of the default terminate() function.
The following program demonstrates how to set a custom handler for unexpected exceptions:
#include <exception>
#include <iostream>
using namespace std;
int main() {
// Set the custom termination handler
set_terminate(myhandler);
17 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
try {
cout << "Inside try block\n";
throw 100; // Throws an integer exception
}
catch (char a) { // This block doesn't catch the int exception
cout << "Inside catch block\n";
}
return 0;
}
Explanation
1. Custom Handler:
2. Uncaught Exception:
o The catch block is designed to handle a char exception, so it doesn’t match the thrown
exception.
3. Result:
o The program calls terminate(), which invokes the custom handler (myhandler).
In C++, constructors can be used in exception handling to initialize and store data related to
the error condition. When creating a user-defined exception class, constructors are often used
to pass and store error-specific data that can later be displayed or used in the error-handling
process.
Key Points
2. Parameterized Constructor:
o Used to pass and store invalid values or details about the error condition when the
exception object is created.
o An object of the user-defined exception class is created with the error-specific data and
then thrown using the throw keyword.
o The catch block defines an object of the user-defined class to handle the exception and
can access the stored error data.
Example
#include <exception>
#include <iostream>
using namespace std;
public:
// Constructor to collect error-specific data
DemoExpt(int a, int b) {
v1 = a;
v2 = b;
}
cout << "Error: Invalid values - " << v1 << " and " << v2 <<
endl;
}
};
int main() {
int a, b;
cout << "Enter two numbers: ";
cin >> a >> b;
try {
if (a < b) {
// Create exception object with invalid values
DemoExpt obj(a, b);
throw obj; // Throw the object
} else {
cout << "Ans = " << (a - b) << endl;
}
}
// Catch block to handle the exception
catch (DemoExpt ex) {
ex.ShowErr();
}
1. User-Defined Class:
o Contains:
20 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
o If the first number (a) is smaller than the second number (b), the program creates a
DemoExpt object with a and b as arguments and throws it.
o The catch block handles the exception by creating a DemoExpt object (ex) and
calling its ShowErr() method to display the error details.
In C++, when an exception is thrown and the program control passes from a try block to a
catch block, the destructors of all automatic (local non-static) objects created since the start
of the try block are called automatically. This process is known as stack unwinding.
Key Points
1. Stack Unwinding:
o It is the process where destructors of all automatic objects are called in reverse order of
their creation during exception handling.
o Automatic objects are local non-static objects that are destroyed when the block or
function in which they are declared terminates.
2. Destructor Behavior:
o Destructors clean up resources, release memory, or perform any necessary final actions
for objects.
o This ensures that resources are not leaked even when an exception is thrown.
3. Exception in Destructors:
o If a destructor throws an exception during stack unwinding and the exception is not
caught, the terminate() function is called, which halts the program execution.
21 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
Example
#include <iostream>
using namespace std;
class Test {
int id;
public:
Test(int id) : id(id) {
cout << "Constructor of object " << id << endl;
}
~Test() {
cout << "Destructor of object " << id << endl;
// Uncommenting the below line will call terminate()
// throw runtime_error("Exception in destructor");
}
};
int main() {
try {
Test t1(1); // Automatic object 1
Test t2(2); // Automatic object 2
cout << "Throwing an exception...\n";
throw runtime_error("Error occurred!");
}
catch (runtime_error &e) {
cout << "Caught exception: " << e.what() << endl;
}
Explanation
1. Constructor Calls:
o Objects t1 and t2 are created in the try block, and their constructors are called.
2. Stack Unwinding:
o When an exception is thrown, control moves to the catch block, and the destructors of
t2 and t1 are called in reverse order of their creation.
3. Exception in Destructor:
o If a destructor throws an exception and it is not handled, the program will terminate by
calling terminate().
In C++, it is possible to throw and catch class objects as exceptions. When dealing with
inheritance in exceptions, special rules apply due to the relationship between base and derived
classes.
Key Points
o If a derived class is thrown as an exception, it can be caught by a catch block for either
the derived class or its base class.
o A catch block for the derived class takes precedence over a base class block.
o Rule: The catch block for the derived class must appear before the block for the base
class.
o If the base class catch block is placed first, the derived class catch block will never be
executed because the base class block matches all derived types.
3. Compiler Behavior:
o The C++ compiler might issue a warning if the base class catch block precedes the
derived class block, but the code will still compile.
Example
#include <iostream>
using namespace std;
int main() {
Derived d; // Object of derived class
try {
throw d; // Throwing an object of derived class
}
catch (Derived &d) { // Catch derived class first
cout << "Caught Derived Exception" << endl;
}
catch (Base &b) { // Catch base class
cout << "Caught Base Exception" << endl;
}
return 0;
}
Explanation
o If the catch block for Base was placed first, it would catch all derived class objects,
and the Derived block would never be executed.
2. Templates
24 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
Templates are a fundamental feature in C++ that allow the creation of generic code. They
enable the definition of functions and classes that can operate with different data types without
requiring separate implementations for each type.
When defining a function or a class, we usually specify the data types of the variables to
match the type of data and operations required.
However, sometimes the same logic can be applied to different types of data.
o Example: Swapping two values, whether int, float, or char, involves the same steps.
Using Generic Programming, a single function or class can be designed to work with any
type of data.
Definition: A programming style where one algorithm is designed to perform specific tasks,
independent of the data type it operates on.
The data type is specified later, during the function call or class instantiation.
Advantages of Templates
1. Code Reusability: Write one code block and use it for various data types.
2. Efficiency: Avoid code duplication for similar operations on different data types.
3. Flexibility: Handle primitive types (int, float, etc.), arrays, pointers, or even user-
defined objects.
4. Scalability: Easily extend functionality to new data types by reusing the same logic.
They are particularly useful for data structures and algorithms, such as sorting,
searching, and stacks, which can work with different types of data.
In simpler terms, it’s about writing algorithms and functions that can handle any data type
without changing the underlying code for each data type. This allows the code to be more
flexible, reusable, and maintainable.
The data type is not hardcoded into the algorithm or function but is defined during
function or class invocation (at runtime).
Types are generic, meaning we use placeholders for types (usually T or typename),
and the actual type is specified when the function or class is called.
In C++, templates provide the mechanism for implementing generic programming. They
allow you to create generic functions and classes that can work with any data type.
Template Function: Allows you to write a function that can work with any data type.
Template Class: Allows you to create a class that can handle any data type.
The template type is used to define variables within the function or class, and the specific data
type is provided when you call the function or instantiate the class.
A function template allows you to define a generic function that can work with any data type.
Instead of specifying a particular data type for function parameters and return types, you can
define a template that allows the function to accept different types of data, such as int, float,
double, or even user-defined types.
A function template is a function that can operate with any data type. It is defined using the
template keyword and a placeholder for the data type, which is typically represented by T or
another generic name.
26 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
class TypeName: This is the placeholder for the data type that will be used in the
function. You can also use typename instead of class. The type name will be specified
when the function is called.
ReturnType: The return type of the function, which will be replaced with the template
type during function call.
When you call a template function, you provide the actual data type for TypeName in the
form of FunctionName<DataType>(arguments).
1. Code Reusability: You can write one function that works with multiple data types,
avoiding code duplication.
2. Flexibility: The same function can work with different types of data, such as integers,
floats, and user-defined types, with no additional code changes.
3. Type Safety: The compiler ensures that the function is used with compatible data types.
4. Maintainability: Having a single function template makes the code easier to maintain
and extend.
#include <iostream>
using namespace std;
int main() {
int p = 4, q = 3, r;
float x = 3.4, y = 1.2, z;
return 0;
}
Explanation:
o The function Add is a template function that can add two values of any data type
(TempType).
o When the function is called in the main() function, the data type (int or float) is
specified, and the compiler generates the appropriate function.
#include <iostream>
using namespace std;
28 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
int main() {
// Example with integers
int intArr[] = {10, 20, 5, 8, 15};
int size = sizeof(intArr) / sizeof(intArr[0]);
cout << "Minimum in integer array: " << findMin(intArr, size) <<
endl;
return 0;
}
Explanation:
o The Min function is a template function that finds the smallest element in an array.
o It works with both int and float types because the data type is specified at the time
of function call (Min<int> for integers, Min<float> for floats).
29 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
o Depending on the user's choice (opt), the function either works with an array of integers
or floats to find the smallest number.
Function overloading is a form of polymorphism in C++, where multiple functions can have
the same name but differ in their parameter types or the number of parameters. This allows
the programmer to define several functions that perform similar operations but on different
types of data.
When using function templates, overloading can also be applied to define a function template
that works with different data types.
If you have a normal function (non-template) and a template function with the same name,
the compiler will use the normal function for exact matches of data types. If there is no exact
match, the template function will be used.
For example:
#include <iostream>
using namespace std;
int main() {
int x = 5, y = 10;
30 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
return 0;
}
Explanation:
o The function add(int, int) is a regular function, so it will be used when the
arguments are of type int.
o For other types like float, the template function add<TempType> is used.
In C++, class templates are used when we need to create a class that can operate on a variety
of data types. A class template allows us to define a class where the data type is specified at the
time of object creation (instantiation), making the class flexible and reusable for different types
of data. Class templates are ideal for generic programming, as they allow the same class to
work with different data types without duplicating the code.
class ClassName: This defines the name of the class template. The class can now use
the TypeName in its data members and member functions to allow flexibility in the
types of data it can work with.
31 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
When creating an object of a class template, we specify the actual data type to be used. The
compiler then replaces the template with the specified data type, generating a new class that
operates on that data type.
ClassName<DataType> object_name;
DataType: This is the type of data (such as int, float, char, etc.) that the class template
will use.
object_name: This is the name of the object created from the class template.
In C++, a class template can accept multiple template parameters, allowing you to create a class
that works with more than one data type. This feature enables you to design classes that can
operate on various types of data and support multiple operations that involve these types.
#include <iostream>
using namespace std;
private:
Type1 first; // First number
Type2 second; // Second number
public:
// Constructor to initialize the numbers
Adder(Type1 f, Type2 s) : first(f), second(s) {}
int main() {
// Creating objects of Adder class with int and float types
Adder<int, float> adder1(5, 3.5);
cout << "Sum (int + float): " << adder1.add() << endl;
return 0;
}
This C++ program demonstrates a class template with multiple parameters. The Adder class
takes two data types as template parameters and adds two numbers of those types.
33 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
The Adder class has a constructor to initialize the two numbers and a method add()
to perform the addition.
In main(), three Adder objects are created with different types (e.g., int and float,
double and int).
In addition to type parameters, non-type parameters can also be used in class templates. These
are values that are passed as arguments to the template. Non-type parameters are typically
constant expressions, such as integers, pointers, or other compile-time constant values.
Non-type parameters are often used when you need to define sizes, limits, or other
constant values for a class template.
A template friend function combines the concepts of templates and friend functions. You can
declare a friend function as a template function to allow it to work with any data type while
still having access to the private members of a class.
To do this:
2. The friend function is also defined as a template, allowing it to work with various
data types.
class ClassName {
// Declare the template friend function
friend ReturnType FriendFunctionName(ClassName<T>&);
};
#include <iostream>
using namespace std;
public:
Box(T l) : length(l) {}
int main() {
Box<int> intBox(5);
Box<double> doubleBox(3.5);
cout << "Area of int box: " << getArea(intBox) << endl;
cout << "Area of double box: " << getArea(doubleBox) << endl;
return 0;
}
In this example:
The Box class is a template class that holds a data member length of type T.
The getArea function is a friend function template that can access the private member
length and calculate the area (assuming the box is square in this case).
The getArea function is a template, meaning it can work with any data type (int,
double, etc.) passed to the Box class.
In C++, two important keywords related to templates are typename and export. These
keywords have specific roles that enhance the functionality and reusability of templates in C++.
Below is an explanation of each keyword:
1. typename Keyword
The typename keyword in C++ serves two primary purposes, both related to templates.
In C++, when defining a template, you can use either the class or typename keyword to declare
a template parameter. Both keywords work identically in this context and specify that a given
type is generic, allowing the template to be used with any data type.
Syntax:
FunctionOrClassDefinition;
Example:
You can also use the class keyword instead of typename. There is no functional
difference between using class and typename in this context. Both are used to specify
the type parameter for the template.
The typename keyword is also used to inform the compiler that a name is a type name rather
than an object or variable. This is particularly important in cases where a name is part of a
template class or when accessing types nested within templates.
Example:
This is necessary because the compiler might not automatically recognize it as a type
due to its dependency on the template parameter.
37 | P a g e © Haris Chaus | ALL RIGHTS ARE RESERVED as per copyright act.
2. export Keyword
The export keyword was introduced in earlier versions of C++ to help with separating the
declaration and definition of template classes or functions. It allows you to declare a template
in one file and then define it in another file, promoting reusability and cleaner code.
Purpose:
The export keyword enables the template to be used across multiple translation units
(source files) without requiring the full definition in each file.
It is used to make templates reusable by declaring them in a header file while keeping
their definitions in a source file.
However, the export keyword is not widely supported in modern C++ compilers and has been
deprecated. Most modern C++ compilers do not implement it due to its complexity and lack
of widespread use. Today, templates are typically declared and defined in header files, and
export is rarely used in practice.
export allows the template definition in one file while still being usable in other files
that include the header with the declaration.