Cpp Module 5: Pointers, Arrays & Exception Handling – BEU B.Tech CSE 3rd Semester Notes
Complete Module 5 notes for BEU B.Tech CSE 3rd Semester (AI & ML). Covers Pointers, Arrays, Dynamic Arrays, Exception Handling, try-throw-catch, custom exception classes, stack unwinding, and all important exam concepts in simple language.
For students of Bihar Engineering University (BEU) pursuing B.Tech 3rd Semester in Computer Science Engineering (CSE) with Artificial Intelligence and Machine Learning (AI/ML) specialization, this module plays a very important role in building core programming skills. The topics covered in this part of the syllabus—pointers, arrays,oid Pointers, Pointer to Class, Pointer to Object, Void Pointer, Arrays. The keywords try, throw and catch. Creating own Exception Classes, Exception Handling Techniques (Terminate the Program, Fix the Error and Continue, Log the Error and Continue), Stack Unwinding.
Module 5 covers three very important concepts in C++: pointers, arrays, and exception handling. These concepts form the foundation for memory management, data handling, and reliable program execution. Pointers help access memory directly and make programs efficient. Arrays allow storing multiple values in continuous memory, making data management simple and structured. Exception handling ensures programs can deal with runtime errors safely without crashing. Together, these topics help students understand how C++ handles memory, data structures, and runtime errors in a professional and secure way.
Pointer
A pointer is a variable that stores the memory address of another variable.
Instead of storing a normal value, a pointer stores where the value is located in memory.
Pointers allow direct memory access, dynamic memory management, faster processing, and flexible data structures. They are one of the most important features of C++ for working with arrays, objects, and classes.
Important Concepts About Pointers
A pointer stores an address, not a value.
The address-of operator (&) gives the memory location of a variable.
The dereference operator (*) is used to access the value stored at the address.
Pointers must be declared with the datatype they point to.
A pointer can point to variables, arrays, objects, and class members.
Pointers allow passing large objects efficiently to functions.
Incorrect pointer handling can cause errors like segmentation faults.
Pointer Syntax
datatype *pointerName;
Breakdown:
datatype→ Type of variable the pointer will point to*→ Declares a pointerpointerName→ Name of the pointer variable
Example:
int *p;
float *q;
char *ch;
Address-of Operator (&)
The & operator is used to get the address of a variable.
int x = 10;
cout << &x; // prints memory address of x
Dereference Operator (*)
The * operator is used to access the value stored at the address pointed by the pointer.
int x = 10;
int *p = &x;
cout << *p; // prints value at address stored in p → 10
Example Program Using Pointer
#include <iostream>
using namespace std;
int main() {
int x = 25;
int *ptr;
ptr = &x; // pointer stores address of x
cout << "Value of x = " << x << endl;
cout << "Address of x = " << &x << endl;
cout << "Pointer ptr stores = " << ptr << endl;
cout << "Value at ptr = " << *ptr << endl;
return 0;
}
Output
Value of x = 25
Address of x = 0x7ffd1234abcd
Pointer ptr stores = 0x7ffd1234abcd
Value at ptr = 25
Code Flow Explanation
A normal integer variable
xis created with value 25.Pointer
ptris declared to store the address of an int.ptr = &x;stores the address of x inside ptr.ptrnow contains the memory location ofx.Using
*ptr, we get the value stored at that location (25).This demonstrates basic pointer usage: declaration, storing address, and dereferencing.
Types of Pointers in C++
Null Pointer
Void Pointer
Wild Pointer
Dangling Pointer
Pointer to Object
Pointer to Class Member
Pointer to Array
Double Pointer (Pointer to Pointer)
Function Pointer
Constant Pointer & Pointer to Constant
Void Pointer
A void pointer is a special pointer in C++ that can store the address of any data type.
Because it has no fixed type, it is known as a generic pointer.
However, since the compiler doesn’t know what type of data it points to, a void pointer cannot be directly dereferenced. It must first be converted (type-cast) into a specific pointer type before accessing the value.
Important Points
A void pointer can point to any data type (int, float, char, object, etc.).
Also called a generic pointer.
Cannot be dereferenced directly because no type information is available.
Must be type-casted to the correct type before accessing the value.
Useful when the datatype is unknown or changes during runtime.
Commonly used in generic functions, memory management, and low-level programming.
Syntax
void *ptr;
Assigning address:
int x = 10;
void *ptr = &x;
Dereferencing requires casting:
cout << *(int*)ptr;
Example: Using Void Pointer
#include <iostream>
using namespace std;
int main() {
int a = 20;
float b = 5.5;
void *ptr; // generic pointer
ptr = &a;
cout << "Value of a = " << *(int*)ptr << endl;
ptr = &b;
cout << "Value of b = " << *(float*)ptr << endl;
return 0;
}
Output
Value of a = 20
Value of b = 5.5
Code Flow Explanation
A void pointer
ptris declared, which can point to any type.First, the address of integer
ais stored inptr.Since void pointers cannot be dereferenced directly, it is type-cast to
(int*)and then dereferenced.Next, the address of float
bis assigned to the same pointer.It is type-cast to
(float*)before printing the value.This shows how a single void pointer can point to different datatypes.
Uses of Void Pointer
Useful in generic programming where datatype is not fixed.
Used internally in functions like
malloc(), which returnvoid*.Helpful in designing generic data structures.
Saves memory because one pointer can point to many types.
Improves flexibility of functions that accept multiple datatypes.
Null Pointer
A null pointer is a pointer that does not point to any valid memory location. It is used to show that the pointer is empty, unused, or intentionally not assigned. A null pointer helps avoid accidental access to garbage memory and is useful for checking whether a pointer is safe to use before dereferencing it.
Important Points
A null pointer stores zero as its address.
It represents an empty or invalid memory reference.
Used to check whether a pointer is pointing to something or not.
Dereferencing a null pointer causes a runtime error.
Commonly used in conditions, linked lists, trees, dynamic memory, and error checking.
Helps prevent use of uninitialized or wild pointers.
Syntax
int *ptr = NULL; // old style
int *ptr = 0; // valid
int *ptr = nullptr; // modern C++ (recommended)
Example
#include <iostream>
using namespace std;
int main() {
int *p = nullptr; // null pointer
if (p == nullptr) {
cout << "Pointer is null, no memory assigned." << endl;
}
return 0;
}
Output
Pointer is null, no memory assigned.
Code Flow Explanation
A pointer
pis initialized withnullptr, meaning it does not point to any variable.The program checks if the pointer is null before using it.
Since
pis null, the message is printed.This prevents errors like dereferencing an invalid pointer.
Uses of Null Pointer
To indicate that a pointer currently has no valid target.
To check whether memory allocation failed.
To mark the end of linked lists or trees.
To safely initialize pointers before assigning them.
To avoid using uninitialized or wild pointers.
Pointer to Class
A pointer to class is a pointer variable that stores the address of an object of a class. Just like pointers to int or float, a class pointer helps access members of the class using the arrow operator (->). This allows working with objects dynamically, passing objects to functions efficiently, and creating arrays of objects on the heap. A class pointer does not store the object itself, but the memory location of the object.
Important Points
A class pointer stores the address of an object.
Declared using the class name followed by
*.Members of the object are accessed using the
->operator.Useful for dynamic object creation using
new.Helps achieve run-time polymorphism when used with virtual functions.
Passing object pointers to functions is faster than passing whole objects.
Can also point to different objects at different times.
Syntax
class ClassName {
public:
int a;
void show();
};
ClassName *ptr; // pointer to class
Assigning address:
ClassName obj;
ptr = &obj;
Accessing members:
ptr->a;
ptr->show();
Example: Pointer to Class
#include <iostream>
using namespace std;
class Student {
public:
int roll;
void display() {
cout << "Roll = " << roll << endl;
}
};
int main() {
Student s; // normal object
s.roll = 10;
Student *ptr; // pointer to class
ptr = &s; // store address of object
ptr->display(); // accessing using pointer
return 0;
}
Output
Roll = 10
Code Flow Explanation
A class Student with a data member roll and a display() function is created.
An object s is declared normally.
A pointer
Student *ptris made to store the address of s.ptr = &s;assigns the object's address to the pointer.Using
ptr->display(), we access the member function with the arrow operator.The function runs and prints the roll number.
Uses of Pointer to Class
Helps in dynamic object creation using
new.Supports run-time polymorphism when pointing to derived objects.
Makes object passing efficient because only address is passed, not the whole object.
Useful in data structures like linked lists, trees, stacks, and queues.
Can point to different objects during execution, increasing flexibility.
Pointer to Object
A pointer to object is a pointer that stores the memory address of an object created either normally or dynamically. Once the object’s address is stored in the pointer, you can access the object’s data members and member functions using the arrow operator (->). Pointer to object is important in situations where objects are created at runtime, passed to functions efficiently, or used for polymorphism.
Important Points
A pointer to object stores the address of an object.
Declared using the class name followed by
*.Members are accessed using
->instead of the dot operator.Can point to objects created normally or using
new.Passing objects using pointers is faster than copying whole objects.
Used in dynamic memory management and run-time polymorphism.
One pointer can point to different objects at different times.
Syntax
ClassName obj; // normal object
ClassName *ptr; // pointer to object
ptr = &obj; // store address of object
ptr->member; // access member
ptr->function(); // call function
Example: Pointer to Object
#include <iostream>
using namespace std;
class Employee {
public:
string name;
int id;
void show() {
cout << "Name: " << name << ", ID: " << id << endl;
}
};
int main() {
Employee e; // normal object
e.name = "Rahul";
e.id = 101;
Employee *ptr; // pointer to object
ptr = &e; // store address of object
ptr->show(); // accessing through pointer
return 0;
}
Output
Name: Rahul, ID: 101
Code Flow Explanation
An Employee object
eis created normally.A pointer
Employee *ptris declared to store the address of an Employee object.ptr = &e;assigns address of object e to the pointer.Using
ptr->show(), the show() function of the object is called through the pointer.The function prints the name and ID stored in object e.
Uses of Pointer to Object
Helps in creating dynamic objects using
new.Supports runtime polymorphism by pointing to derived class objects.
Makes function calls efficient with large objects.
Essential for implementing data structures like linked lists, trees, stacks, queues.
Allows flexible object handling since the pointer can change the object it points to.
Pointer to Member Function
A pointer to member function is a special pointer in C++ that stores the address of a member function of a class. Unlike normal function pointers, a member function pointer is always connected to a specific class because member functions need an object to run. The pointer only stores the address of the function, and an object (or pointer to object) is required to call it.
Important Points
Stores the address of a member function of a class.
Must be declared using the class name.
Cannot be called directly—needs an object or object pointer.
Accessed using
object.*pointerorobjectPtr->*pointer.Useful for callback mechanisms and dynamic function selection.
Syntax is more complex than normal function pointers.
Return type, class name, and parameter types must match exactly.
Syntax
Declaration:
return_type (ClassName::*ptrName)(parameter_list);
Assigning address:
ptrName = &ClassName::functionName;
Calling using object:
(obj.*ptrName)(arguments);
Calling using pointer to object:
(objPtr->*ptrName)(arguments);
Example: Pointer to Member Function
#include <iostream>
using namespace std;
class Test {
public:
void show() {
cout << "Member function called using pointer" << endl;
}
};
int main() {
Test t;
Test *ptrObj = &t;
// pointer to member function
void (Test::*funcPtr)() = &Test::show;
// calling through object
(t.*funcPtr)();
// calling through object pointer
(ptrObj->*funcPtr)();
return 0;
}
Output
Member function called using pointer
Member function called using pointer
Code Flow Explanation
The class Test contains a member function show().
A member function pointer
funcPtris declared with the class name Test.The address of the function is assigned using
&Test::show.Using
(t.*funcPtr)(), the function is called through a normal object.Using
(ptrObj->*funcPtr)(), the function is called through a pointer to object.Both calls successfully run the show() function and print the output.
Uses of Pointer to Member Function
Useful in callback mechanisms inside classes.
Allows dynamic selection of which member function to call at runtime.
Used in event-driven programming and state machines.
Helpful in frameworks and libraries where behavior depends on runtime conditions.
Supports flexible code by separating object and function logic.
Array
An array is a collection of elements of the same datatype stored in continuous (contiguous) memory locations and accessed using an index. It allows storing multiple values under one name, making data handling easier. Arrays help manage lists, tables, and large sets of values efficiently.
Important Points
Stores multiple values of the same type.
Memory is contiguous, meaning elements are stored one after another.
Index starts from 0 (first element) and goes till size–1.
Accessing elements is fast because each element has a fixed position in memory.
The array size must be known before use (static array).
Array name represents the address of the first element.
Can be 1-D, 2-D, or multi-dimensional.
Works well with pointers since array elements sit in continuous memory.
Syntax
datatype arrayName[size];
Examples:
int arr[5];
float marks[10];
char name[20];
Accessing Elements
arr[0] = 10;
arr[1] = 20;
cout << arr[0];
Example Program: Basic Array
#include <iostream>
using namespace std;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
cout << "First element = " << arr[0] << endl;
cout << "Third element = " << arr[2] << endl;
return 0;
}
Output
First element = 10
Third element = 30
Code Flow Explanation
An array of 5 integers is declared and initialized.
arr[0]accesses the first element (10).arr[2]accesses the third element (30).Because arrays have contiguous memory, each element is accessed using its index position.
Types of Arrays in C++
One-Dimensional Array
Two-Dimensional Array (Matrix)
Multi-Dimensional Array
Array of Objects
Pointer and Array combination
One-Dimensional Array
A one-dimensional array stores a list of elements of the same datatype in a single row of continuous memory. It allows you to store multiple values using one variable name and access them using an index. One-dimensional arrays are the simplest form of arrays and are commonly used for storing lists, marks, numbers, names, and other linear data.
Important Points
Stores data in a single row.
All elements must be of the same datatype.
Index starts from 0 and ends at size – 1.
Memory is continuous, allowing fast element access.
Array size must be declared before use.
Array name gives the address of the first element.
Can be initialized during declaration or later.
Syntax
datatype arrayName[size];
Example:
int arr[5];
Initialization
int arr[5] = {10, 20, 30, 40, 50};
Or partially:
int arr[5] = {10, 20}; // remaining elements become 0
Or at runtime:
int arr[3];
arr[0] = 5;
arr[1] = 10;
arr[2] = 15;
Accessing Elements
arr[0];
arr[1];
arr[4];
Example Program
#include <iostream>
using namespace std;
int main() {
int num[5] = {5, 10, 15, 20, 25};
cout << "First element = " << num[0] << endl;
cout << "Last element = " << num[4] << endl;
return 0;
}
Output
First element = 5
Last element = 25
Code Flow Explanation
A one-dimensional array
numof size 5 is declared.It stores values: 5, 10, 15, 20, 25 in continuous memory.
num[0]gives the first element (5).num[4]gives the last element (25).Elements are accessed using index numbers because the array uses fixed positions in memory.
Uses of One-Dimensional Arrays
Storing lists like marks, prices, scores, ages, etc.
Processing sequential data.
Searching and sorting operations.
Basic data structures like stacks and queues use 1-D arrays internally.
Passing multiple values to functions in a simple way.
Two-Dimensional Array
A two-dimensional array stores data in the form of rows and columns, similar to a table or matrix. It can be imagined as an array of one-dimensional arrays. Two-dimensional arrays are useful for storing structured data like matrices, tables, grids, marksheets, and seating charts. Each element is accessed using two indexes: one for the row and one for the column.
Important Points
Stores data in rows and columns (matrix form).
All elements must be of the same datatype.
Declared using two sizes: rows and columns.
Indexing starts from 0 for both rows and columns.
Memory is stored continuously row-wise.
Access requires two indexes:
array[row][column].Useful for matrices, 2D tables, and structured datasets.
Syntax
datatype arrayName[rows][columns];
Example:
int matrix[3][3];
Initialization
Complete initialization:
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
Single-line initialization:
int matrix[2][3] = {1, 2, 3, 4, 5, 6};
Partial initialization:
int matrix[2][3] = { {1, 2}, {3} };
Accessing Elements
matrix[0][0]; // first row, first column
matrix[1][2]; // second row, third column
Example Program
#include <iostream>
using namespace std;
int main() {
int arr[2][3] = {
{10, 20, 30},
{40, 50, 60}
};
cout << "Element at [0][1] = " << arr[0][1] << endl;
cout << "Element at [1][2] = " << arr[1][2] << endl;
return 0;
}
Output
Element at [0][1] = 20
Element at [1][2] = 60
Code Flow Explanation
A 2×3 matrix is created with 2 rows and 3 columns.
{10, 20, 30}is the first row,{40, 50, 60}is the second row.Index
[0][1]accesses row 0, column 1 (value = 20).Index
[1][2]accesses row 1, column 2 (value = 60).Two-dimensional arrays allow storing and accessing tabular data efficiently.
Uses of Two-Dimensional Arrays
Storing matrices in mathematics.
Storing tables such as marksheets or bills.
Creating board games like chess, tic-tac-toe, sudoku.
Representing grids, pixel data, and maps in programming.
Handling multi-dimensional datasets in applications.
Multi-Dimensional Array
A multi-dimensional array is an array with more than two dimensions, such as 3-D, 4-D, or higher. While a 2-D array stores data in rows and columns, a multi-dimensional array stores data in multiple levels, similar to stacking multiple 2-D tables on top of each other. Multi-dimensional arrays are useful in advanced problems like 3-D matrices, cube-shaped data, scientific calculations, graphics, and simulations.
Important Points
Contains more than two dimensions (3-D, 4-D, etc.).
All elements must be of the same datatype.
Elements are stored in continuous memory, row-wise.
Access requires multiple indexes:
arr[i][j][k]....Can represent cube-like or layered structures.
Used for applications like 3-D modeling, simulations, and scientific data.
Syntax
datatype arrayName[size1][size2][size3] ... ;
Example of a 3-D array:
int arr[2][3][4];
This means:
2 blocks
each block has 3 rows
each row has 4 columns
Initialization
int arr[2][2][2] = {
{
{1, 2},
{3, 4}
},
{
{5, 6},
{7, 8}
}
};
Accessing Elements
arr[0][1][1]; // first block, second row, second column
arr[1][0][0]; // second block, first row, first column
Example Program
#include <iostream>
using namespace std;
int main() {
int cube[2][2][2] = {
{
{1, 2},
{3, 4}
},
{
{5, 6},
{7, 8}
}
};
cout << "cube[0][1][0] = " << cube[0][1][0] << endl;
cout << "cube[1][0][1] = " << cube[1][0][1] << endl;
return 0;
}
Output
cube[0][1][0] = 3
cube[1][0][1] = 6
Code Flow Explanation
A 3-D array
cubecontains 2 blocks, each with 2 rows and 2 columns.Each element is accessed using three indexes.
cube[0][1][0]means block 0 → row 1 → column 0 → value 3.cube[1][0][1]means block 1 → row 0 → column 1 → value 6.Multi-dimensional arrays allow storing multi-layered data structures.
Uses of Multi-Dimensional Arrays
3-D mathematical matrices.
Representing cube structures.
Pixel values in 3-D graphics or animations.
Scientific calculations and simulations.
Storing layered data like images, maps, or games.
Representing positions in 3-D space (x, y, z).
Dynamic Arrays
A dynamic array is an array whose size is decided at runtime, not at compile time. It is created using dynamic memory allocation with the new operator. Dynamic arrays are useful when the number of elements is not known in advance. The memory for a dynamic array is taken from the heap, and it remains until it is manually released using delete.
Important Points
Size is decided during program execution, not before.
Created using the
newoperator.Memory is stored in the heap, not in the stack.
Accessing elements is the same as a normal array.
Must release memory using
delete[]to avoid memory leaks.Useful when the required size changes or depends on user input.
Can be combined with classes, pointers, and functions easily.
Syntax
datatype *arrayName = new datatype[size];
Releasing Memory
delete[] arrayName;
Example: Creating a Dynamic Array
#include <iostream>
using namespace std;
int main() {
int n;
cout << "Enter size: ";
cin >> n;
int *arr = new int[n]; // dynamic array
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
cout << "Array elements: ";
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
delete[] arr; // freeing memory
return 0;
}
Output (example)
Enter size: 5
Array elements: 1 2 3 4 5
Code Flow Explanation
Program asks the user for the array size at runtime.
Memory is created dynamically using
new int[n].Values are stored in the dynamically allocated array.
Elements are printed using normal indexing.
Memory is released using
delete[] arrto avoid memory leaks.Dynamic arrays allow flexible sizing based on program needs.
Uses of Dynamic Arrays
When size is not fixed or depends on user input.
For large data that cannot fit in stack memory.
Dynamic data structures like dynamic lists, matrices, and graphs.
Useful in competitive programming and large applications.
Helpful when creating objects dynamically using classes.
Relationship Between Pointers and Arrays
In C++, the name of an array acts like a constant pointer to the first element of the array. This means arrays and pointers are closely related, and in many situations, a pointer can be used to access array elements just like array indexing. Although arrays and pointers are not the same thing, they behave similarly because the array name holds the base address, and pointer arithmetic lets you move through the array.
Important Points
The array name represents the address of the first element.
If
arris an array, thenarris equal to&arr[0].A pointer can store the address of an array’s first element.
Array elements can be accessed using either indexing (
arr[i]) or pointer notation (*(ptr + i)).Arrays use continuous memory, so pointer arithmetic works naturally.
You cannot change the array name (it is a constant pointer), but you can change a pointer.
Passing arrays to functions actually sends a pointer to the first element.
Syntax
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr; // ptr stores address of first element
Accessing Elements (Two Ways)
Using array indexing:
arr[2];
Using pointer arithmetic:
*(ptr + 2);
Both give the same result.
Example
#include <iostream>
using namespace std;
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr; // pointer stores base address
cout << "arr[0] = " << arr[0] << endl;
cout << "ptr[0] = " << ptr[0] << endl;
cout << "arr[2] = " << arr[2] << endl;
cout << "*(ptr + 2) = " << *(ptr + 2) << endl;
return 0;
}
Output
arr[0] = 10
ptr[0] = 10
arr[2] = 30
*(ptr + 2) = 30
Code Flow Explanation
The array
arrcontains 5 integers in continuous memory.When
ptr = arr;is used, the pointer stores the address of the first element.ptr[0]andarr[0]give the same value because they refer to the same location.*(ptr + 2)accesses the third element, similar toarr[2].This shows that pointers can traverse arrays using pointer arithmetic.
Why Arrays and Pointers Are Related
The array name acts like a constant pointer to element 0.
Pointer arithmetic allows moving from one element to the next.
Passing an array to a function sends a pointer, not the full array.
Many operations with arrays depend on pointer behavior.
Exception Handling
Exception handling in C++ is a mechanism that allows a program to detect and handle runtime errors without crashing. Instead of stopping the program immediately when an error occurs, C++ gives you a way to “catch” the error and handle it safely. This makes programs more reliable, secure, and user-friendly. Exception handling separates normal program logic from error-handling logic so that errors do not disrupt the flow of the program.
Important Points
Handles runtime errors like division by zero, invalid input, file failure, etc.
Uses three main keywords: try, throw, and catch.
try block contains code where an exception might occur.
throw keyword sends (raises) the exception when an error is detected.
catch block receives and processes the thrown exception.
More than one catch block can be used.
You can create your own exception classes.
Exception handling improves program stability and reliability.
Keywords Used in Exception Handling
try
A try block contains the code where an exception may occur.
If an exception happens inside the try block, control jumps to the matching catch block.
Syntax:
try {
// code that might cause an error
}
throw
The throw keyword generates an exception and transfers control to the catch block.
Syntax:
throw value; // value can be int, string, or object
catch
A catch block handles the exception thrown by the throw statement.
Syntax:
catch(type variable) {
// handling code
}
Basic Example Division by Zero
#include <iostream>
using namespace std;
int main() {
int a = 10, b = 0;
try {
if (b == 0) {
throw "Division by zero error";
}
cout << a / b;
}
catch (const char *msg) {
cout << msg;
}
return 0;
}
Output
Division by zero error
Code Flow Explanation
The try block checks if
bis zero.Since b is zero,
throwsends an exception message.The catch block receives this message and prints it.
The program does not crash; it handles the error safely.
Creating Own Exception Classes
C++ allows you to create your own exception classes to handle errors in a more meaningful and organized way. Instead of throwing simple integers or strings, you can create a class that stores detailed information about the error. This gives better control, clearer messages, and more professional error handling. User-defined exception classes work just like built-in exceptions but give you the freedom to define your own error type, message, and behavior.
Important Points
A user-defined exception class is created like a normal class.
It usually stores an error message or details about the problem.
Objects of this class are thrown using
throw.The catch block receives the object and accesses its data members.
Helps in creating structured and readable error-handling systems.
Can include functions to display custom error messages.
Allows multiple types of custom errors in large programs.
Basic Syntax
class ExceptionName {
public:
string message;
ExceptionName(string msg) {
message = msg;
}
};
Throwing the exception:
throw ExceptionName("Error message");
Catching it:
catch (ExceptionName e) {
cout << e.message;
}
Example: Creating and Using a Custom Exception Class
#include <iostream>
using namespace std;
class MyException {
public:
string message;
MyException(string msg) {
message = msg;
}
};
int main() {
int age;
try {
cout << "Enter age: ";
cin >> age;
if (age < 0) {
throw MyException("Age cannot be negative");
}
cout << "Valid age entered: " << age << endl;
}
catch (MyException e) {
cout << "Exception: " << e.message << endl;
}
return 0;
}
Output (example)
Enter age: -5
Exception: Age cannot be negative
Code Flow Explanation
A custom exception class
MyExceptionis created.It stores an error message inside the
messagevariable.In the try block, the program takes input for age.
If age is negative,
throw MyException("Age cannot be negative")sends a custom exception object.The catch block receives the object and prints its message.
This allows clean and meaningful error handling without crashing the program.
Why Use Custom Exception Classes?
Gives more descriptive and meaningful error messages.
Helps manage different types of errors with different classes.
Provides better structure for large programs.
Can include extra information like error codes, timestamps, or recovery steps.
Improves debugging and error tracing.
Multiple Catch Blocks
Multiple catch blocks allow you to handle different types of exceptions separately.
A single try block can have more than one catch block, and each catch block handles a different datatype or different error condition.
This gives more control, because each type of exception can be processed in its own way.
Important Points
One try block can be followed by multiple catch blocks.
Only the matching catch block gets executed.
Order matters: more specific exceptions should be written before general ones.
Helps handle different error types individually.
You cannot have two catch blocks for the same exact type.
Syntax
try {
// risky code
}
catch (int x) {
// handles integer exception
}
catch (char c) {
// handles character exception
}
catch (string s) {
// handles string exception
}
Example
#include <iostream>
using namespace std;
int main() {
try {
throw 10; // throws an int
}
catch (int x) {
cout << "Caught integer: " << x << endl;
}
catch (char c) {
cout << "Caught character: " << c << endl;
}
catch (string s) {
cout << "Caught string: " << s << endl;
}
return 0;
}
Output
Caught integer: 10
Explanation
The try block throws an integer (10).
The catch block that matches
intruns.Other catch blocks are ignored.
catch(...) (General Catch Block)
The catch(...) block is a catch-all handler.
It catches any type of exception that does not match previous catch blocks.
It is used as the last catch block for unexpected or unknown errors.
Important Points
Catches any exception if no other catch matches.
Must always appear last in the sequence of catch blocks.
Used for unknown or unexpected errors.
Does not provide information about the actual exception type.
Syntax
catch(...) {
// handles any exception
}
Example
#include <iostream>
using namespace std;
int main() {
try {
throw 5.5; // throws double
}
catch (int x) {
cout << "Integer exception" << endl;
}
catch (...) {
cout << "Unknown exception caught" << endl;
}
return 0;
}
Output
Unknown exception caught
Explanation
The try block throws a double value.
There is no catch block for double.
The
catch(...)block catches it because it handles all exceptions.
When to Use Multiple Catch Blocks and catch(...)
Use multiple catch blocks when:
You want to handle different error types differently.
You need specific messages for int, string, float, custom exception, etc.
Use catch(...) when:
You want to catch all remaining unknown exceptions.
You want to prevent the program from crashing due to unexpected errors.
Exception Handling Techniques
Exception handling techniques are the different ways a program can respond after detecting an error during execution. Depending on how serious the error is and what the program needs to do next, the programmer can choose to stop the program, fix the problem, or record the error and continue running. These techniques help in managing runtime errors safely without crashing the program.
1. Terminate the Program
This technique is used when the error is serious, and the program cannot continue safely.
After catching the exception, the program displays a message and stops execution.
When used
Division by zero
File not found
Invalid memory access
Critical data missing
Example
try {
throw "Fatal Error!";
}
catch (const char *msg) {
cout << msg;
// stop the program
}
The program ends after the catch block because the error is not recoverable.
2. Fix the Error and Continue
In this technique, the catch block corrects the mistake and allows the program to keep running.
This is used when the error is simple or predictable, and you can take corrective action.
When used
Invalid user input
Negative values when only positive values are allowed
Asking user to retry the operation
Example
int x;
try {
cout << "Enter positive number: ";
cin >> x;
if (x < 0) {
throw x;
}
cout << "You entered: " << x;
}
catch (int n) {
cout << "Negative value entered. Converting to positive." << endl;
x = -n; // fixing the error
cout << "Using fixed value: " << x;
}
The program corrects the value and continues.
3. Log the Error and Continue
In this technique, the error is recorded (logged) for reference, but the program continues running normally.
The catch block does not fix the problem—it only stores or displays the error message.
When used
Large applications where errors must be recorded
Background tasks
Server applications
Situations where small errors should not stop the entire program
Example
try {
throw "File missing";
}
catch (const char *msg) {
cout << "Error logged: " << msg << endl;
// continue program normally
}
The program logs the error and continues executing the remaining code.
Summary of Techniques
Terminate the Program: stop execution after handling a serious error.
Fix the Error and Continue: correct the mistake and keep running.
Log the Error and Continue: record the error without interrupting program flow.
These techniques help programmers handle different types of errors in the most appropriate way based on the situation.
Simple Example of All Three Exception Handling Techniques
#include <iostream>
using namespace std;
int main() {
int n;
try {
cout << "Enter number: ";
cin >> n;
// Technique 1: Terminate the Program
if (n == 0) {
throw 0;
}
// Technique 2: Fix the Error and Continue
if (n < 0) {
throw -1;
}
// Technique 3: Log the Error and Continue
if (n > 100) {
throw "big";
}
cout << "Valid number: " << n << endl;
}
catch (int x) {
if (x == 0) {
cout << "Error: Zero is not allowed. Program terminated." << endl;
return 0; // terminate program
}
if (x == -1) {
cout << "Negative number fixed to positive." << endl;
n = -n; // fix error
cout << "Fixed value: " << n << endl;
}
}
catch (const char *msg) {
cout << "Warning: Number too large (logged). Continuing..." << endl;
}
cout << "Program continues..." << endl;
return 0;
}
Output Examples (Short)
If user enters 0
Error: Zero is not allowed. Program terminated.
If user enters -5
Negative number fixed to positive.
Fixed value: 5
Program continues...
If user enters 150
Warning: Number too large (logged). Continuing...
Program continues...
If user enters 20
Valid number: 20
Program continues...Stack Unwinding
Stack unwinding is the process in C++ where, after an exception is thrown, the program goes backward through the function call stack, destroying all the local variables of each function, until it finds a matching catch block.
It “unwinds” the stack by cleaning up each function one by one, ensuring no memory is leaked and all objects are properly destroyed.
This happens automatically whenever an exception is thrown.
Important Points
Occurs when an exception is thrown inside a nested function call.
C++ destroys all local variables in each function while moving backward.
Stops unwinding when it finds a matching catch block.
Ensures proper cleanup of objects before exiting the function.
Prevents memory leaks and ensures safe exit from functions.
If no catch block is found, the program terminates.
Only automatic (local) objects are destroyed; dynamic memory must be manually handled.
Simple Concept Explanation
Imagine three functions: A → B → C
If an error happens in C and C cannot handle it:
C ends and its local variables are destroyed
Control moves to B → B’s local variables destroyed
Then to A → if A has the correct catch block, the error is handled
This backward cleaning is called Stack Unwinding
Example Program
#include <iostream>
using namespace std;
void C() {
cout << "Inside C" << endl;
throw 1; // exception thrown here
}
void B() {
cout << "Inside B" << endl;
C(); // call C
}
void A() {
cout << "Inside A" << endl;
B(); // call B
}
int main() {
try {
A(); // start from A
}
catch (int x) {
cout << "Exception caught in main" << endl;
}
return 0;
}
Output
Inside A
Inside B
Inside C
Exception caught in main
Code Flow (Stack Unwinding Explained)
main() calls A
A calls B
B calls C
C throws an exception
C ends → its local variables destroyed
Control goes back to B → B ends → its variables destroyed
Then A ends → its variables destroyed
Finally reaches main → catch block handles the exception
This backward cleanup is stack unwinding
Why Stack Unwinding Is Important
Prevents memory leaks by destroying objects in reverse order.
Ensures safe cleanup of resources when an error occurs.
Helps the program exit from multiple function calls safely.
Makes exception handling reliable across nested function calls.
Module 5 of the Bihar Engineering University (BEU) syllabus is one of the most important parts of the B.Tech 3rd Semester CSE course for all branches including AI, ML, Data Science, Software Engineering, and CSIT. Understanding pointers, arrays, dynamic memory, and exception handling helps students build strong programming fundamentals that are essential for advanced subjects and real-world coding. These notes are prepared in a simple and clear format to make learning easier for BEU students and to support quick exam revision. With a proper grasp of Module 5 concepts, CSE students can confidently move forward into more complex topics and practical applications in upcoming semesters.