MODULE-4
[Link] Abraham
Assistant Professor
Dept of CSE
Syllabus
Function Templates and Exception Handling:
Function templates with multiple arguments,
Class templates, templates and inheritance,
Exceptional Handling, Use of exceptional
handling. File handling Concepts.
Function of Template
■ Define function templates that could be used to create a family of
functions with different argument types.
■ The general format of a function template is:
template<class T>
returntype functioname (arguments of type T) // with type T
// wherever appropriate
{
// .....
// ..... }
// Body of function
■ The function template syntax is similar to that of the class
template except that we are defining functions instead of classes.
■ We must use the template parameter T as and when necessary, in
the function body and in its argument list.
■ The following example declares a swap() function template that
will swap two values of a given type of data.
template<class T>
void swap(T&x, T&y)
T temp = x;
x = y;
y = temp;
}
■ This essentially declares a set of overloaded functions, one for
each type of data.
■ We can invoke the swap() function like any ordinary function.
For example, we can apply the swap() function as follows:
void f(int m,int n,float a,float b)
swap(m,n); // swap two integer values
swap(a,b); // swap two float values
// .....
■ This will generate a swap() function from the function template
for each set of argument types.
#include <iostream> cout << "m and n after swap: " << m << " "
using namespace std; << n << "\n";
template <class T> cout << "a and b before swap: " << a << "
" << b << "\n";
void myswap(T &x, T &y)
myswap(a,b);
{
cout << "a and b after swap: " << a << " "
T temp = x; << b << "\n";
x = y; }
y = temp; int main()
} {
void fun(int m,int n,float a,float b) fun(100,200,11.22,33.44);
{ return 0;
cout << "m and n before swap: " << m << }
" " << n << "\n";
myswap(m,n);
m and n before swap: 100 200
m and n after swap: 200 100
a and b before swap: 11.22 33.44
a and b after swap: 33.44 11.22
Function Templates with Multiple Parameters
■ Like template classes, we can use more than one generic data type in the
template statement, using a comma-separated list as shown below:
template<class T1, class T2, ...>
returntype functionname (arguments of types T1, T2,...)
.....
..... (Body of function)
.....
}
int main()
#include <iostream>
{
#include <string>
cout<<"Calling function template with
using namespace std;
int and character strin parame\n";
template<class T1, class T2>
display(1999, "EBG");
void display(T1 x, T2 y) cout<<"Calling function template with
{ float and integer type parame\n";
cout << x << " " << y << "\n"; display(12.34, 1234);
} return 0;
}
Calling function template with int and character string
parameter 1999 EBG
Calling function template with float and integer type
parameter 12.34 1234
Overloading of Template Functions
■ A template function may be overloaded either by template
functions or ordinary functions of its name. In such cases, the
overloading resolution is accomplished as follows:
1. Call an ordinary function that has an exact match.
2. Call a template function that could be created with an exact
match.
3. Try normal overloading resolution to ordinary functions and call
the one that matches.
■ An error is generated if no match is found.
■ Note that no automatic conversions are applied to arguments on
the template functions.
■ Program shows how a template function is overloaded with an
explicit function.
#include <iostream> {
cout<<"Overloaded Template Display 2:
#include <string> "<<x<<", " <<y<<"\n";
}
using namespace std;
void display(int x) //overloaded generic display
template <class T> function
{
void display(T x) //overloaded cout<<"Explicit display: "<<x<<"\n";
template function display }
{ int main()
{
cout<<"Overloaded Template
display(100);
Display 1: "<<x<<"\n";
display(12.34);
} display(100,12.34);
template <class T, class T1> display('C');
return 0;
void display(T x, T1 y) //overloaded }
template function display
Explicit display: 100
Overloaded Template Display 1: 12.34
Overloaded Template Display 2: 100, 12.34
Overloaded Template Display 1: C
Member Function Templates
■ When creating a class template for a vector, defining all member
functions as inline inside the class isn't necessary.
■ They can be defined outside the class as well.
■ However, since the member functions depend on the template
type parameter, they must themselves be function templates.
■ Their general form looks like this:
Template<class T>
returntype classname <T> :: functionname(arglist)
// .....
// Function body
// .....
The vector class template and its member functions are redefined as
follows:
#include <iostream> // Constructor 1
using namespace std;
template<class T>
template<class T>
class vector vector<T>::vector(int m)
{ {
T* v; size = m;
int size;
public:
v = new T[size];
vector(int m); // creates vector of for(int i = 0; i < size; i++)
size m
v[i] = 0;
vector(T* a, int m); // creates vector
from array }
T operator*(vector & y); // dot
product
};
// Constructor 2 // Dot product
template<class T> template<class T>
vector<T>::vector(T* a, int m) T vector<T>::operator*(vector &
{ y)
size = m; {
v = new T[size]; T sum = 0;
for(int i = 0; i < size; i++) for(int i = 0; i < size; i++)
v[i] = a[i]; sum += v[i] * y.v[i];
} return sum;
}
int main()
{
int arr1[] = {1, 2, 3};
int arr2[] = {4, 5, 6};
vector<int> v1(arr1, 3);
vector<int> v2(arr2, 3);
cout << "Dot product = " << (v1 * v2) << endl;
return 0;
}
o/p
Dot product = 32
New
i v[i] y.v[i] Multiply Add to sum
sum
0 1 4 1×4 = 4 sum += 4 4
1 2 5 2×5 = 10 sum += 10 14
2 3 6 3×6 = 18 sum += 18 32
Class template
■ A class template in C++ is a way to create a class that can work with
different data types without rewriting the code. It allows generic
programming.
template <class T> void setData(T value) {
data = value;
class ClassName { }
T data; void showData() {
cout << "Data: " << data << endl;
public: }
};
#include <iostream> int main() {
using namespace std;
template <class T>
Sample<int> obj1;
class Sample { [Link](10);
T value;
public:
[Link]();
void set(T v) { Sample<float> obj2;
value = v;
[Link](3.14);
}
void display() { [Link]();
cout << "Value = " << value << endl;
return 0;
}
}; }
Value = 10
Value = 3.14
Non-type Template Arguments
■ We have seen that a template can have multiple
arguments. It is also possible to use non-type arguments.
■ That is, in addition to the type argument T, we can also
use other arguments such as strings, function names,
constant expressions and built-in types
■ Consider the following example:
template<class T, int size>
class array
T a[size]; // automatic array initialization
// .....
// .....
};
■ This template supplies the size of the array as an argument.
■ This implies that the size of the array is known to the compiler at
the compile time itself.
■ The arguments must be specified whenever a template class is
created.
■ Example:
array<int,10> a1; // Array of 10 integers
array<float,5> a2; // Array of 5 floats
array<char,20> a3; // String of size 20
The size is given as an argument to the template class.
#include <iostream> int main() {
using namespace std; Box<5> b1; // SIZE = 5
// Non-type template argument used in Box<10> b2; // SIZE = 10
a class [Link]();
template <int SIZE> [Link]();
class Box { return 0;
public: }
void showSize() { Size is: 5
cout << "Size is: " << SIZE << Size is: 10
endl;
}
};
Exception Handling
“Exception handling is a mechanism used to detect and handle runtime errors
so that the normal flow of the program does not crash”.
1. Synchronous Exceptions
■ Caused by program errors (e.g., out-of-range index, overflow).
■ Handled by C++ exception mechanism.
2. Asynchronous Exceptions
■ Caused by external events (e.g., keyboard interrupt).
■ Not handled by C++ exception mechanism.
Purpose of Exception Handling
1. Detect and report exceptional circumstances.
2. Allow separate error-handling code.
Exception Handling Process
1. Detect the problem – Exception occurs.
2. Throw the exception – Inform an error has occurred.
3. Catch the exception – Receive the error.
4. Handle the exception – Take corrective action
Error Handling Code Segments
1. Try block: Detects errors and throws exceptions.
2. Catch block: Catches exceptions and handles them.
1. try
■ Used to start a block of code that might
cause an error.
■ This block is called a try block.
2. throw
■ Used to throw an exception (i.e., report
the error) when something goes wrong in
the try block.
3. catch
■ Used to catch and handle the exception
thrown by the try block.
Working
try {
■ The code that might cause an error
// Code that might cause
is written inside a try block.
an exception
■ If an error happens, it is thrown throw error; // Throw an
using the throw keyword. exception
■ The error is then caught and }
handled in the catch block. catch (type error) {
// Code to handle the
■ The catch block must follow right
exception
after the try block.
}
#include <iostream> else // There is an exception
using namespace std;
{
int main()
{ throw(x); // Throws int object
int a,b; }
cout << “Enter Values of a and b \n”; }
cin >> a;
catch(int i) // Catches the exception
cin >> b;
int x = a-b; {
try cout<<“Exception caught: DIVIDE BY
{ ZERO\N”;
if(x != 0)
}
{
cout << “Result(a/x) = ” << a/x << cout << “END”;
“\n”; return 0;
} }
1. First Run:
■ The denominator is not zero.
■ No exception is thrown.
■ The catch block is skipped.
■ Program continues normally after the catch block.
2. Second Run:
■ The denominator x becomes zero.
■ A division-by-zero exception is thrown using the object x.
■ Since x is an int, the catch block with an int type catches the
exception.
■ A message is displayed to show that a division-by-zero occurred.
Invoking Function That Generates Exception
We are inside the try block
We are inside the function
Result = -3
We are inside the function
Caught the exception
Throwing Mechanism
■ When an exception that is desired to be handled is detected, it is
thrown using the throw statement in one of the following forms:
■ throw(exception);
■ throw exception;
■ throw;
■ an exception // used for rethrowing
■ The operand object exception may be of any type, including
constants.
■ It is also possible to throw objects not intended for error handling
■ When an exception is thrown, it will be caught by the catch
statement associated with the try block.
■ That is, the control exits the current try block, and is transferred
to the catch block after that try block
■ Throw point can be in a deeply nested scope within a try block or
in a deeply nested function call.
■ In any case, control is transferred to the catch statement
Catching Mechanism
1. Exception-handling code is written inside catch blocks.
2. A catch block looks like catch(type arg) { /* code to handle exception */ }
3. type defines the kind of exception it can handle (e.g., int, char, double).
4. arg is the optional name of the exception object, which can be used inside
the block.
5. If the type of thrown exception matches the type in the catch, that block
runs.
6. After the catch block finishes, control moves to the next statement after
all catch blocks.
7. If no catch block matches the thrown exception, the program terminates
abnormally.
8. If a catch block doesn’t match, it is simply skipped.
Multiple Catch Statements
1. A single try block can be followed by multiple catch blocks to
handle different exception types.
3. When an exception is thrown, the program checks each catch in
order.
4. The first matching catch is executed; all others are skipped.
5. If no match is found, the program terminates.
6. If multiple catch blocks can handle the same type, only the first
matching one runs
#include <iostream> catch (int x) { // catches integer exceptions
using namespace std;
cout << "Error: Division by zero! Value of b
int main() { = " << x << endl;
try {
}
int a, b;
cout << "Enter two numbers: ";
catch (const char* msg) { // catches string
exception
cin >> a >> b;
if (b == 0) cout << "Error: " << msg << endl;
throw b; // throwing integer }
exception
catch (double d) { // catches double exception
if (a < 0)
throw "Negative number not allowed"; cout << "Error: Number too large! Value =
// throwing string exception " << d << endl;
if (a > 100) }
throw 1.5; // throwing float
exception cout << "Program ended normally." << endl;
cout << "Result = " << a / b << endl; return 0;
} }
Input :a = 10, b = 0
a = -5, b = 2
a = 150, b = 10
a = 20, b = 5
Catching all exception
1. Sometimes, it’s hard to predict all the types of exceptions a program might
throw.
2. In such cases, instead of writing separate catch blocks for each type, you
can use a generic catch.
3. A generic catch block catches all types of exceptions, regardless of their
type.
4. Syntax for catch-all:
catch(...) {
// code to handle any exception
5. This catch(...) must be placed after all specific catch blocks, or
used alone if needed.
#include <iostream> int main()
using namespace std;
{
void test(int x)
{
cout << “Testing Generic Catch \n”;
try test(-1);
{ test(0);
if(x == 0) throw x; // int test(1);
if(x == -1) throw ‘x’; // char
return 0;
if(x == 1) throw 1.0; // float
} }
catch(...) // catch all The output Testing Generic Catch
{ Caught an exception
cout << “Caught an exception \n”;
Caught an exception
}
}
Caught an exception
Class types as exception handling
■ Instead of throwing basic types like int, char, or double, you can throw
objects of a class as exceptions.
■ This is useful when you want to represent different types of errors in a
structured way.
■ You can create a custom error class with data members to store error
details (like code and description).
■ The thrown object is then caught using a catch block that matches the class
type.
#include <iostream> try {
using namespace std; cout << "Trying to divide..." << endl;
class Error { int a = 10, b = 0;
public: if (b == 0) {
throw Error("Cannot divide by zero!");
string msg;
}
cout << "Result = " << a / b << endl;
Error(string m) { }
msg = m; catch (Error e) {
} cout << "Exception caught: " << [Link] << endl;
}; }
int main() { cout << "End of program." << endl;
return 0;
}
Output:
Trying to divide...
Exception caught: Cannot divide by zero!
End of program.
Rethrowing An Exception
■ When an exception is rethrown, it will not be caught by the same
■ catch statement or any other catch in that group.
■ Rather, it will be caught by an appropriate catch in the outer try/catch
sequence only.
■ A catch handler itself may detect and throw an exception. Here again, the
exception thrown will not be caught by any catch statements in that group.
■ It will be passed on to the next outer try/catch sequence for processing.
#include <iostream> cout << "End of function\n\n";
using namespace std; }
void divide(double x, double y) int main()
{ {
cout << "Inside function\n"; cout << "Inside main\n";
try { try {
if (y == 0.0) divide(10.5, 2.0);
throw y; // throwing double divide(20.0, 0.0);
else }
cout << "Division = " << x / y << "\n"; catch (double) {
} cout << "Caught double inside main\n";
catch (double) { }
cout << "Caught double inside function\n"; cout << "End of main\n";
throw; // rethrowing the same return 0;
exception
}
}
Inside main
Inside function
Division = 5.25
End of function
Inside function
Caught double inside function
Caught double inside main
End of main
Exceptions in Constructors and Destructor
1. A constructor is used to initialize an object when it is created.
2. If an exception is thrown during the constructor execution:
o The object is not fully constructed.
o The destructor is not called.
o Any memory or resources allocated before the exception will not be released
automatically, causing a memory leak.
3. Therefore, you must handle the exception inside the constructor to release
resources before the exception leaves the constructor.
4. After cleaning up, you can rethrow the exception to be handled
in the main() function or higher-level logic.
5. Example (pseudo-code):
} catch (e) {
class A { // clean up
public: delete d1; // free the
memory
A() {
throw e; // rethrow
try { exception
// allocate memory or resources }
}
throw e; // some error occurred
};
6. If you only catch the exception in main(): o You miss the chance to clean
up memory allocated in the constructor. o This results in memory leak.
7. So, catch the exception inside the constructor, clean up, then rethrow it for
proper handling in main()
#include <iostream> int main() {
using namespace std;
class Test { try {
int x;
Test t(-5); // constructor throws
public:
Test(int value) { }
if (value < 0)
throw value; // exception in catch (int) {
constructor
x = value; cout << "Exception caught in
cout << "Constructor completed\n"; main\n";
}
}; }
}
Exception caught in main
Exceptions in Destructors:
1. Destructors are called automatically when an object goes out of scope or
is deleted.
2. If an exception is thrown from a destructor: (must not throw exception)
o It can be dangerous if another exception is already being handled (this is
called stack unwinding).
o This can cause the program to abort immediately (std::terminate is called).
3. Memory leaks can happen if the destructor throws before releasing all
resources.
4. Best practice: Always handle exceptions inside the destructor.
5. Do not let exceptions escape from a destructor.
6. Example: // destructor code
throw; // throws something
class A {
} catch (...) {
public: // handle the exception safely
}
~A() {
}
try { };
#include <iostream> int main() {
using namespace std; Test t;
class Test { cout << "Main ends normally\n";
public: }
~Test() {
try { Output:
cout << "Cleaning resources\n"; Main ends normally
// risky cleanup code Cleaning resources
}
catch (...) {
cout << "Exception suppressed in
destructor\n";
}
}
};
File handling
■ File handling in C++ allows you to store, read, and modify data in files
(like .txt files).
■ C++ uses streams to perform file operations.
■ The header file required for file handling is
#include <fstream>
■ File Stream Classes
■ Opening a File You can open a file using:
1. Constructor:
ifstream fin("[Link]");
2. open() method:
ofstream fout;
[Link]("[Link]");
Opening a file using the constructor string line;
#include <iostream> while (getline(fin, line)) {
#include <fstream> cout << line << endl;
using namespace std; }
int main() { [Link](); // Close the file
ifstream fin("[Link]"); // Open return 0;
file using constructor
}
if (!fin) {
cout << "Error opening input
file!" << endl;
return 1;
}
Opening a file using the open() fout << "Hello, this is a C++ file
method
#include <iostream> handling example." << endl;
#include <fstream> fout << "Writing data to a file." <<
using namespace std;
endl;
int main() {
ofstream fout; [Link](); // Close the file
[Link]("[Link]"); // Open
file using open() method return 0;
if (!fout) {
}
cout << "Error opening output
file!" << endl;
return 1;
}
Reading from a File
Writing to a File
#include <fstream> #include <fstream>
#include <iostream> [Link]();
using namespace std;
return 0;
int main() { using namespace std;
}
ofstream fout("[Link]"); // create int main() {
or overwrite file ifstream fin("[Link]");
fout << "Hello, File!"; string line;
[Link](); // always close the file while (getline(fin, line)) {
cout << line << endl;
return 0;
}
}
File Modes
■ File modes define how a file is opened:
Example:
fstream file;
[Link]("[Link]", ios::in | ios::out)
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// 1. ios::out (Write mode) - creates or truncates file
ofstream fout1("[Link]", ios::out);
fout1 << "This is written using ios::out mode.\n";
[Link]();
// 2. ios::app (Append mode) - adds data at the end
ofstream fout2("[Link]", ios::app);
fout2 << "This line is appended using ios::app mode.\n";
[Link]();
// 3. ios::ate (At end) - opens file and moves pointer to end
ofstream fout3("[Link]", ios::ate);
fout3 << "This line is written at the end using ios::ate mode.\n";
[Link]();
// 4. ios::trunc (Truncate) - clears existing content
ofstream fout4("[Link]", ios::out | ios::trunc);
fout4 << "File content is cleared and rewritten using ios::trunc.\n";
[Link]();
// 5. ios::binary (Binary mode)
ofstream fout5("[Link]", ios::out | ios::binary);
int num = 12345;
[Link](reinterpret_cast<char*>(&num), sizeof(num)); // Write binary data
[Link]();
// 6. ios::in (Read mode)
ifstream fin("[Link]", ios::in);
string line;
cout << "Reading [Link]:\n";
// 4. ios::trunc (Truncate) - clears existing content
ofstream fout4("[Link]", ios::out | ios::trunc);
fout4 << "File content is cleared and rewritten using ios::trunc.\n";
[Link]();
// 5. ios::binary (Binary mode)
ofstream fout5("[Link]", ios::out | ios::binary);
int num = 12345;
[Link](reinterpret_cast<char*>(&num), sizeof(num)); // Write binary data
[Link]();
// 6. ios::in (Read mode)
ifstream fin("[Link]", ios::in);
string line;
cout << "Reading [Link]:\n";
while (getline(fin, line)) {
cout << line << endl;
}
[Link]();
return 0;
}
Closing a File
■ Always close the file after operations:
■ [Link]();
Checking File Status
if (!fin) {
cout << "File could not be opened!";
}
Reading/Writing Character by Character
#include <iostream> cout << "Contents of [Link]:\n";
#include <fstream> while ([Link](ch)) { // Read one character at
a time
using namespace std;
cout << ch; // Display character
int main() { }
char ch; [Link](); // Close input file
ifstream fin("[Link]"); // Open file cout << endl << "Writing a character 'A' to
for reading [Link]..." << endl;
if (!fin) { ofstream fout("[Link]"); // Open file for writing
(truncates existing content)
cout << "Error opening file for
reading!" << endl; if (!fout) {
cout << "Error opening file for writing!" <<
return 1; endl;
} return 1;
}
[Link]('A'); // Write single character to file
[Link](); // Close output file
cout << "Character written successfully." << endl;
return 0;