0% found this document useful (0 votes)
7 views26 pages

C++ OOP Exam Solutions and Concepts

The document provides a comprehensive explanation of Object Oriented Programming concepts using C++, covering topics such as classes, objects, operator overloading, and inheritance. It includes definitions, comparisons between programming paradigms, and examples of code demonstrating these concepts. Additionally, it discusses the ambiguity in multiple inheritance and the implications of access specifiers in class derivation.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views26 pages

C++ OOP Exam Solutions and Concepts

The document provides a comprehensive explanation of Object Oriented Programming concepts using C++, covering topics such as classes, objects, operator overloading, and inheritance. It includes definitions, comparisons between programming paradigms, and examples of code demonstrating these concepts. Additionally, it discusses the ambiguity in multiple inheritance and the implications of access specifiers in class derivation.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

The following is a well-explained solution to all questions from the examination paper on

Object Oriented Programming Using C++ (Paper: IT-252E).

UNIT-I

1. (a) Define object and classes? Differentiate between procedure


oriented and object oriented programming.

Object and Classes

●​ Class: A class is a blueprint, template, or prototype from which objects are created. It is
a logical construct that defines the properties (data members) and behaviors (member
functions/methods) that all objects of that type will have.
○​ Nature: Abstract.
○​ Memory: No memory is allocated when a class is defined.
●​ Object: An object is a real-world entity and an instance of a class. Once a class is
defined, you can create multiple objects from it, each holding its own copy of the class's
properties.
○​ Nature: Concrete.
○​ Memory: Memory is allocated when an object is instantiated, and it stores the
actual data.
○​ Analogy: A class is like the blueprint for a car, while an object is the actual car
built from that blueprint.

Difference between Procedure Oriented Programming (POP) and Object


Oriented Programming (OOP)

Procedural Programming (POP) and Object-Oriented Programming (OOP) are two distinct
programming paradigms.
Feature Procedure Oriented Object Oriented
Programming (POP) Programming (OOP)
Focus Focuses on procedures Focuses on objects. The
(functions). The program is program is organized around
divided into functions. data and objects.
Design Approach Follows a top-down approach. Follows a bottom-up
approach.
Data & Access Data is often stored in global Data is encapsulated within
variables, making it accessible objects. Objects control
from anywhere in the program. access to their data through
Data security is low. methods, ensuring high data
integrity and security.
Data Hiding Not supported. Supported (through access
specifiers like private and
protected).
Reusability Limited reusability; complex High reusability through
functionalities might require concepts like Inheritance.
code duplication.
Polymorphism Not directly supported. Supported (through function
overloading, overriding, and
virtual functions).
Examples C, Pascal, FORTRAN. C++, Java, Python, C#.

1. (b) Draw comparison between Overloading vs. Overriding. Give a


brief note on overriding methods.

Comparison between Function Overloading and Function Overriding

Feature Function Overloading Function Overriding


Definition Multiple functions share the A derived class provides its
same name but have own specific implementation
different parameter lists of a method that is already
(different number or types of defined in its base class.
arguments).
Scope Occurs within a single class or Occurs in an inheritance
across globally-scoped hierarchy (Base class and
functions. Derived class).
Function Signature Must have different signatures Must have the same signature
(parameters). (name, parameters, and return
type).
Polymorphism Example of Compile-time Example of Run-time
Polymorphism (or Static Polymorphism (or Dynamic
Binding). Binding).
Resolution Resolved at compile time. Resolved at run time.
Brief Note on Overriding Methods

Function overriding is a mechanism in C++ that is essential for achieving run-time


polymorphism (Dynamic Binding).
●​ Mechanism: When a derived class redefines a function that is present in its base class
with the exact same name, return type, and parameter list, it is called method
overriding.
●​ Requirement for Polymorphism: To enable run-time polymorphism, the function in the
base class must be declared with the virtual keyword.
●​ Behavior: When a base class pointer or reference points to a derived class object,
calling the virtual function will execute the derived class's overridden version. This
allows the program to decide which function to call at run-time based on the actual
object type, not the pointer/reference type.
●​ Purpose: It allows derived classes to customize or specialize the behavior inherited
from the base class, making programs flexible and extensible.

2. (a) What is a Structure? Why we need structure definition? How to


access members of Structures?

What is a Structure?

A structure (struct) in C++ is a user-defined data type that allows you to group related
variables of different data types under a single name. Each variable within a structure is
called a member.
●​ Key Difference from Class: By default, members of a C++ struct are public, whereas
members of a C++ class are private.

Why We Need Structure Definition?

We need structure definition for the following reasons:


1.​ Grouping Related Data: To logically bind together a collection of heterogeneous data
items that represent a single entity.
○​ Example: To represent an address, you need to group a street name (string), a
house number (integer), and a zip code (integer). Without a structure, you would
have to manage these as separate, unrelated variables.
2.​ Creating a New Data Type: The structure definition creates a new data type identifier
that can be used to declare variables (struct variables/objects) in the same way built-in
types (like int or float) are used.
3.​ Passing Data Easily: A structure allows you to pass a whole record of information to a
function by passing a single structure variable, simplifying function calls and
maintaining data integrity.

How to Access Members of Structures?

Structure members are accessed using the dot operator (.) when operating on a direct
structure variable or using the arrow operator (->) when operating on a pointer to a
structure.
Example:

C++

#include <iostream>​

// Structure Definition​
struct Student {​
int roll_no;​
std::string name;​
};​

int main() {​
// 1. Access using a direct variable (Dot operator)​
Student s1;​
s1.roll_no = 101; // Accessing member using dot operator​
[Link] = "Alice"; // Accessing member using dot operator​

std::cout << "Student: " << [Link] << ", Roll No: " << s1.roll_no << std::endl;​

// 2. Access using a pointer (Arrow operator)​
Student* s2_ptr = new Student;​
s2_ptr->roll_no = 102; // Accessing member using arrow operator​
s2_ptr->name = "Bob"; // Accessing member using arrow operator​

std::cout << "Student: " << s2_ptr->name << ", Roll No: " << s2_ptr->roll_no << std::endl;​

delete s2_ptr;​
return 0;​
}​

2. (b) Define the 'this' pointer, with an example, indicate the steps
involved in referring to members of the invoking object.

Definition of the 'this' Pointer

The this pointer is a special, constant pointer that is automatically passed as a hidden
argument to every non-static member function of a class.
●​ Purpose: It holds the address of the object on which the member function is invoked
(the invoking object).
●​ Function: It enables the member function to access the specific data members and call
other member functions of that particular object.

Steps Involved in Referring to Members of the Invoking Object

When a member function is called on an object, the following steps happen implicitly:
1.​ Implicit Argument Passing: The address of the invoking object is automatically passed
as a hidden argument (the this pointer) to the member function.
2.​ Member Access: Inside the member function, every direct reference to a non-static
member variable (e.g., age or name) is implicitly translated by the compiler into an
expression using the this pointer (e.g., this->age or this->name).
3.​ Explicit Use Cases: While the compiler performs the access implicitly, the this pointer
is explicitly used in C++ for scenarios like:
○​ Distinguishing between a member variable and a local variable/parameter that
have the same name (name conflict).
○​ Returning a reference or pointer to the invoking object from a function (often
used for chaining function calls).

Example Demonstrating 'this' Pointer

C++

#include <iostream>​
#include <string>​

class Student {​
private:​
std::string name;​
int roll_no;​

public:​
// Constructor to initialize members​
Student(std::string name, int roll_no) {​
// Explicitly using 'this' to distinguish between the member​
// variable (this->name) and the parameter (name)​
this->name = name; ​
this->roll_no = roll_no;​
}​

void displayInfo() const {​
// Implicit use: 'name' is internally translated to 'this->name'​
std::cout << "Name: " << name << ", Roll No: " << roll_no << std::endl;​
}​

// Function that returns a reference to the invoking object​
Student& updateRollNo(int new_roll_no) {​
// Use 'this' to modify the member of the invoking object​
this->roll_no = new_roll_no; ​
return *this; // Return the object pointed to by 'this'​
}​
};​

int main() {​
Student s("Chandan", 44003);​
[Link](); // Invoked on object 's', so 'this' inside points to 's'​

// Chaining function calls using the returned '*this'​
[Link](1605)​
.displayInfo(); // First updateRollNo is called, then displayInfo on the updated 's'​

return 0;​
}​
UNIT-II

3. Write the fundamentals of operator overloading. Draw comparison


between operator functions as class members and friend functions.
Write a program to overload binary operators.

Fundamentals of Operator Overloading

Operator Overloading is a feature in C++ that allows the redefinition or customization of the
behavior of existing operators (like +, -, *, ++, etc.) when used with user-defined data types
(classes or structs). It allows objects of a class to interact using traditional operators, making
the code more intuitive and readable.
Key Fundamentals:
1.​ Syntax: Operator overloading is achieved by defining a special function called an
operator function. The general syntax is:​
C++​
ReturnType operatorSymbol (ArgumentList) { ... }​

○​ operator is a keyword.
○​ Symbol is the operator being overloaded (e.g., +, *, <<).
2.​ Cannot Invent New Operators: Only pre-existing C++ operators can be overloaded.
3.​ At least one User-Defined Operand: An overloaded operator function must have at
least one operand that is of a user-defined type (class or struct).
4.​ Preserved Arity and Precedence: The precedence, associativity, and arity (number of
operands) of the original operator cannot be changed.
5.​ Non-Overloadable Operators: A few operators cannot be overloaded, notably the
Scope Resolution Operator (::), Member Access Operator (.), Member Pointer Selector
(.*), and the Ternary/Conditional Operator (?:).

Comparison between Operator Functions as Class Members and Friend


Functions

Operator functions can be implemented either as a member function of the class or as a


friend function (non-member) of the class.
Feature Member Function Operator Friend Function Operator
Call Context Called on an object (e.g., obj1 Called like a regular function
+ obj2). The left operand must (e.g., operator+(obj1, obj2)).
be an object of the class.
Arguments for Binary Op. Takes one explicit argument Takes two explicit arguments
(the right operand). The left (both the left and right
operand is implicitly the operands).
invoking object (*this).
Arguments for Unary Op. Takes no explicit arguments Takes one explicit argument
(the operand is the invoking (the operand).
object *this).
Access to Private Data Can directly access Can directly access
private/protected members of private/protected members of
the invoking object (*this). the object(s) passed to it
because it is declared as a
friend.
Suitable For Most common operators, Operators where the left
especially assignment (=), operand is a non-class type
function call (()), subscript ([]), (e.g., overloading << for
and ->. std::cout where the left
operand is std::ostream).

Program to Overload Binary Operators (Addition of Complex Numbers)

The following program overloads the binary operator + to add two Complex number objects.

C++

#include <iostream>​

class Complex {​
private:​
float real;​
float imag;​

public:​
// Constructor​
Complex(float r = 0.0, float i = 0.0) : real(r), imag(i) {}​

// Overload the binary '+' operator as a member function​
Complex operator+(const Complex& other) {​
// 'this' (left operand) is implicitly the object on which the function is called (c1)​
// 'other' (right operand) is the argument (c2)​

// Create a new Complex object with the sum of real and imaginary parts​
Complex temp;​
[Link] = this->real + [Link];​
[Link] = this->imag + [Link];​
return temp;​
}​

void display() const {​
std::cout << real;​
if (imag >= 0) {​
std::cout << " + " << imag << "i";​
} else {​
std::cout << " - " << -imag << "i";​
}​
std::cout << std::endl;​
}​
};​

int main() {​
Complex c1(10, 5);​
Complex c2(5, 3);​

// This statement calls the overloaded operator+: Complex c3 = [Link]+(c2);​
Complex c3 = c1 + c2; ​

std::cout << "Complex Number 1: ";​
[Link](); // 10 + 5i​

std::cout << "Complex Number 2: ";​
[Link](); // 5 + 3i​

std::cout << "Sum (c1 + c2): ";​
[Link](); // 15 + 8i​

return 0;​
}​
4. What is the ambiguity that arises in multiple inheritance? Discuss
with examples, the implications of deriving a class from an existing
class by the 'public' and 'protected' access specifiers.

Ambiguity in Multiple Inheritance (The Diamond Problem)

Multiple Inheritance is a feature where a derived class inherits from two or more base
classes.
The primary ambiguity that arises is known as the "Diamond Problem" or Inheritance
Ambiguity, which occurs when:
1.​ Two or more base classes have member functions or data members with the exact
same name.
2.​ A derived class inherits from these multiple base classes.
3.​ An object of the derived class attempts to access that similarly named member.
In this scenario, the compiler cannot determine which base class's member should be
accessed, leading to ambiguity and a compilation error.
Example of Ambiguity:

C++

class BaseA {​
public:​
void print() { /* A's print */ }​
};​

class BaseB {​
public:​
void print() { /* B's print */ }​
};​

class Derived : public BaseA, public BaseB {​
// Derived has inherited two functions named print()​
};​

int main() {​
Derived d;​
// ERROR: Ambiguous call to 'print()'.​
// Compiler doesn't know whether to call BaseA::print or BaseB::print.​
// [Link](); ​

// Resolution: Use the scope resolution operator (::)​
[Link]::print(); // OK: Explicitly calls A's function​
[Link]::print(); // OK: Explicitly calls B's function​
return 0;​
}​

Note: The Diamond Problem (where a class inherits a member multiple times from a common
ancestor via two paths) is a more complex type of ambiguity which is resolved using Virtual
Inheritance.

Implications of Public and Protected Access Specifiers (Visibility Modes)

When a derived class inherits from a base class, the visibility mode (using public, protected,
or private keywords) determines how the inherited members of the base class will be
accessible in the derived class and by outside objects.
Base Class Access Visibility Mode Derived Class Access Accessible by Object
of Derived Class?
public public Remains public Yes
protected public Remains protected No
private public Remains private No
public protected Becomes protected No
protected protected Remains protected No
public private Becomes private No

Discussion and Example:


1.​ Public Derivation (class Derived : public Base):
○​ Implication: Preserves the highest level of access for external objects.
○​ Rule: The access specifier of the base class member remains the same in the
derived class. public members of the base remain public, and protected members
remain protected.
○​ Result: Public members of the base class are accessible to an object of the
derived class.
2.​ Protected Derivation (class Derived : protected Base):
○​ Implication: The public interface of the base class is hidden from the outside
world but is available to all future derived classes (grandchildren).
○​ Rule: Both public and protected members of the base class become protected in
the derived class.
○​ Result: No member (public or protected) of the base class can be accessed by an
object of the derived class from outside the class hierarchy.
C++ Example:

C++

#include <iostream>​

class Base {​
public:​
int pub_val = 1;​
protected:​
int pro_val = 2;​
private:​
int pri_val = 3;​
};​

// 1. Public Derivation​
class PublicDerived : public Base {​
public:​
void accessBaseMembers() {​
std::cout << "Public Derived can access: " << std::endl;​
std::cout << " pub_val: " << pub_val << std::endl; // OK (public -> public)​
std::cout << " pro_val: " << pro_val << std::endl; // OK (protected -> protected)​
// std::cout << pri_val << std::endl; // ERROR: private members are never inherited​
}​
};​

// 2. Protected Derivation​
class ProtectedDerived : protected Base {​
public:​
void accessBaseMembers() {​
std::cout << "Protected Derived can access: " << std::endl;​
std::cout << " pub_val: " << pub_val << std::endl; // OK (public -> protected)​
std::cout << " pro_val: " << pro_val << std::endl; // OK (protected -> protected)​
}​
};​

int main() {​
PublicDerived pd;​
ProtectedDerived prd;​

// Public Derivation Test​
std::cout << "--- Public Derivation ---" << std::endl;​
[Link]();​
std::cout << "Access via object: pd.pub_val = " << pd.pub_val << std::endl; // OK (public
member)​
// pd.pro_val; // ERROR: protected in derived class​

// Protected Derivation Test​
std::cout << "--- Protected Derivation ---" << std::endl;​
[Link]();​
// prd.pub_val; // ERROR: pub_val is 'protected' in ProtectedDerived​

return 0;​
}​

UNIT-III

5. (a) Write a C++ program demonstrating use of the pure virtual


function with the use of base and derived classes.

Pure Virtual Function and Abstract Class

A Pure Virtual Function is a virtual function declared in a base class that has no definition
(no implementation) in the base class. It is declared by assigning = 0 to its declaration: virtual
ReturnType functionName(Arguments) = 0;.
●​ Abstract Class: A class containing at least one pure virtual function is known as an
Abstract Base Class.
●​ Implication: You cannot create objects of an abstract class.
●​ Purpose: The pure virtual function forces all derived classes to provide their own
implementation for that function, thus defining a common interface that all derived
classes must adhere to.
C++ Program Example (Shape Interface):

C++

#include <iostream>​
#include <cmath>​

// Abstract Base Class: Shape​
class Shape {​
public:​
// Pure Virtual Function (Interface): Forces derived classes to implement area()​
virtual double area() = 0; ​

// Regular Virtual Function​
virtual void displayInfo() {​
std::cout << "This is a generic shape." << std::endl;​
}​

// A pure virtual function requires a virtual destructor for proper cleanup​
virtual ~Shape() {} ​
};​

// Derived Class 1: Circle​
class Circle : public Shape {​
private:​
double radius;​
public:​
Circle(double r) : radius(r) {}​

// Must override the pure virtual function 'area'​
double area() override {​
return M_PI * radius * radius;​
}​

void displayInfo() override {​
std::cout << "Circle with radius " << radius << std::endl;​
}​
};​

// Derived Class 2: Rectangle​
class Rectangle : public Shape {​
private:​
double length;​
double width;​
public:​
Rectangle(double l, double w) : length(l), width(w) {}​

// Must override the pure virtual function 'area'​
double area() override {​
return length * width;​
}​
};​

int main() {​
// Shape s; // ERROR: Cannot create object of abstract class 'Shape'​

// Use Base Class Pointer for Polymorphism​
Shape* s_ptr1 = new Circle(5.0);​
Shape* s_ptr2 = new Rectangle(4.0, 6.0);​

std::cout << "--- Polymorphic Behavior ---" << std::endl;​

// Calls Circle::area()​
std::cout << "Area of Circle: " << s_ptr1->area() << std::endl; ​

// Calls Rectangle::area()​
std::cout << "Area of Rectangle: " << s_ptr2->area() << std::endl; ​

// Calls Circle::displayInfo() (Demonstrates virtual function call)​
s_ptr1->displayInfo();​

delete s_ptr1;​
delete s_ptr2;​

return 0;​
}​

5. (b) Write a note on dynamic binding.

Dynamic Binding (Late Binding)

Dynamic Binding (also known as Late Binding or Run-time Binding) is a mechanism in


Object-Oriented Programming where the determination of which function to execute is made
at run-time rather than at compile-time.
1.​ Mechanism: Dynamic binding in C++ is achieved through the use of Virtual Functions
and Inheritance.
○​ When a function is declared as virtual in a base class and is overridden in a
derived class, the decision of which version to call is deferred until the program is
executing.
2.​ How it Works:
○​ The compiler creates a Virtual Table (vtable) for any class that has virtual
functions.
○​ The object of such a class contains a hidden pointer, the Virtual Pointer (vptr),
which points to the class's vtable.
○​ When a virtual function is called through a base class pointer or reference, the
vptr is used to look up the correct function address in the vtable of the actual
object type at run-time, thus ensuring the most derived function is called.
3.​ Key Enabler: It is the key enabler of run-time polymorphism, allowing a single
base-class interface to be used to process objects of different derived types.
Benefits:
●​ Flexibility and Extensibility: New derived classes can be added without modifying the
existing code that uses the base class pointer, making the system extensible.
●​ Cleaner Code: It eliminates the need for manual conditional checks (if-else or switch
statements) based on the object's type to call the correct function.
Contrast: In contrast, Static Binding (or Early Binding) resolves function calls at compile
time, typically used for non-virtual functions, overloaded functions, and non-pointer/reference
calls.

6. (a) What is sequential access file? How to read and update data in
sequential access file?

What is a Sequential Access File?

A sequential access file is a type of file organization where data records are stored and
retrieved one after another in a linear sequence.
●​ Order of Access: To access the $n$-th data item, you must first read (or skip) the
previous $n-1$ data items in the file.
●​ Starting Point: Reading a sequential file typically begins from the start of the file.
●​ Example: A simple text file where lines are processed one by one is a common example
of a sequential file.

How to Read Data in a Sequential Access File

Reading data involves opening the file and reading data items iteratively until the end of the
file is reached.
Steps to Read:
1.​ Open the File: Create an object of the ifstream (input file stream) class and open the
file in read mode.
2.​ Read Iteratively: Use the stream extraction operator (>>) or functions like getline() or
read() to read the data one record or item at a time.
3.​ Check for EOF: The reading process continues in a loop until the End-Of-File (EOF)
marker is detected (e.g., by checking the stream state).
4.​ Close the File: Close the file to release the resources.
C++ Reading Example (assuming file is [Link]):

C++

#include <fstream>​
#include <iostream>​
int main() {​
std::ifstream file("[Link]"); // Open file for reading​
int number;​
while (file >> number) { // Reads sequentially until EOF is reached​
std::cout << "Read: " << number << std::endl;​
}​
[Link]();​
return 0;​
}​

How to Update Data in a Sequential Access File

Updating a record in a sequential file is generally inefficient and involves a multi-step process
because you cannot directly jump to a record and overwrite it without affecting the
surrounding data, especially if the record size changes.
Steps to Update (Simulated Update):
1.​ Open Source and Temporary Files: Open the original file in input mode (ifstream) and
create a new temporary file in output mode (ofstream).
2.​ Sequential Read and Write: Read records sequentially from the source file.
3.​ Identify and Modify:
○​ If the current record is not the one to be updated, write it directly to the
temporary file.
○​ If the current record is the one to be updated, read it, perform the necessary
modification, and then write the modified record to the temporary file.
4.​ Close and Replace: After reaching EOF, close both files. Then, delete the original
source file and rename the temporary file to the name of the original file. This
effectively updates the data by replacing the entire file.
6. (b) Discuss about stream input, stream output, stream
manipulators and stream error states.

Stream Input and Stream Output

In C++, I/O operations are handled using the concept of streams. A stream is an abstraction
that represents a flow of data between a program and an I/O device (like a keyboard, screen,
or file).
●​ Stream Input (istream): A stream that facilitates the flow of data from an input source
to the program.
○​ Operator: Uses the extraction operator (>>).
○​ Common Objects: std::cin (standard input, usually keyboard), std::ifstream (file
input stream).
○​ Example: std::cin >> var;
●​ Stream Output (ostream): A stream that facilitates the flow of data from the program
to an output destination.
○​ Operator: Uses the insertion operator (<<).
○​ Common Objects: std::cout (standard output, usually screen), std::cerr (standard
error), std::ofstream (file output stream).
○​ Example: std::cout << "Value: " << var;

Stream Manipulators

Stream Manipulators are functions or objects used to modify the state of an I/O stream,
thereby changing how the data is formatted, read, or written. They are typically used with the
insertion (<<) or extraction (>>) operators.
Type Manipulator Header Description Example
No Arg std::endl <iostream> Inserts a newline cout << "Hi" <<
character and endl;
flushes the
output buffer.
std::hex, std::oct, <iostream> Sets the numeric cout << hex <<
std::dec base for integer 100; (Outputs 64)
I/O.
std::fixed, <iostream> Sets the cout << fixed <<
std::scientific floating-point 12.34;
notation.
With Arg std::setw(int w) <iomanip> Sets the minimum cout << setw(10)
field width for the << 123;
next output
operation.
std::setfill(char c) <iomanip> Sets the character cout << setfill('*')
used to fill the << 123;
empty space
when setw is
used.
std::setprecision(i <iomanip> Sets the number cout <<
nt p) of digits to be setprecision(2) <<
displayed for 3.14159;
floating-point
values.

Stream Error States

Every stream object maintains a set of internal flags (error states) to monitor the status of the
I/O operations. These flags can be checked using member functions to determine the success
or failure of an operation.
Function Error State Description
good() goodbit Returns true if no errors or
end-of-file condition has been
reached. The stream is ready
for I/O.
fail() failbit or badbit Returns true if an operation
failed (e.g., trying to read an
integer but encountering a
letter). Indicates a potential
data issue.
bad() badbit Returns true if the stream is
corrupted or an irreparable
error occurred (e.g., a file
cannot be opened).
eof() eofbit Returns true if the end of the
input stream (End-Of-File) has
been reached.
UNIT-IV

7. (a) What are function templates? With an example, show how to


overload template functions.

What are Function Templates?

A Function Template is a mechanism in C++ that allows a single function definition to


operate with generic data types. It is a blueprint or formula for creating a function; the actual
function (called a template specialization) is generated by the compiler when it encounters a
call to the template with specific data types.
●​ Syntax: Defined using the template keyword followed by template parameter list
enclosed in angle brackets (<>).​
C++​
template <typename T> // or template <class T>​
T functionName(T arg1, T arg2) { ... }​

○​ T is a placeholder for a data type (generic type parameter).


●​ Purpose: To achieve generic programming (or type-agnostic programming), allowing
the same logic to be applied to different data types without rewriting the code for each
type.

Overloading Template Functions

Function templates can be overloaded by:


1.​ Another function template (with a different signature).
2.​ A non-template function (a regular function).
The compiler follows a specific set of rules (overload resolution) to choose the best
function/template match:
1.​ Exact Match (Non-Template): The compiler first looks for a non-template function
that provides an exact match for the function call arguments. If found, it is chosen.
2.​ Exact Match (Template): If no exact non-template match is found, the compiler tries
to generate a template specialization that provides an exact match.
3.​ Conversions (Non-Template): The compiler then considers non-template functions
that can be matched after applying standard type conversions (e.g., char to int).
Example: Overloading a Template Function with a Non-Template Function
C++

#include <iostream>​

// 1. Function Template: Can handle any type T​
template <typename T>​
void displayMax(T a, T b) {​
std::cout << "Template (Generic): Max is " << (a > b ? a : b) << std::endl;​
}​

// 2. Non-Template Function: Specific overload for 'int' type​
void displayMax(int a, int b) {​
std::cout << "Non-Template (Specific): Max is " << (a > b ? a : b) << std::endl;​
}​

int main() {​
// Case 1: Call with 'int' arguments​
// The non-template function is preferred over the template specialization ​
// because a non-template function takes precedence in overload resolution.​
displayMax(10, 20); // Output: Non-Template (Specific)...​

// Case 2: Call with 'double' arguments​
// No specific non-template function for 'double', so the template is used.​
displayMax(10.5, 5.5); // Output: Template (Generic)...​

// Case 3: Call with 'char' arguments​
// No specific non-template function for 'char', so the template is used.​
displayMax('a', 'z'); // Output: Template (Generic)...​

return 0;​
}​

7. (b) Write a detailed note on class template and non-type


parameters.

Class Template
A Class Template is a design for creating generic classes that can work with any data type. It
allows the class structure to be defined once, and the compiler then generates specific class
types when an object is instantiated with a concrete data type.
●​ Syntax: Similar to function templates, a class template uses the template keyword
followed by template parameters.​
C++​
template <typename T>​
class MyGenericClass {​
T data;​
// ... member functions using T ...​
};​

●​ Instantiation: To use a class template, you must specify the actual data type(s) inside
angle brackets when creating an object.
○​ Example: MyGenericClass<int> int_obj; and MyGenericClass<std::string>
string_obj;
●​ Use Case: The most common example is the C++ Standard Template Library (STL)
containers, such as std::vector<T>, std::list<T>, and std::map<Key, Value>.

Non-Type Parameters in Templates

In addition to type parameters (like typename T or class T), C++ templates can also accept
non-type parameters. A non-type template parameter is a placeholder for a constant value
that must be known at compile time.
●​ Types: Non-type parameters can be integral types (like int, long), enumeration types,
pointers or references to objects/functions, and, since C++20, floating-point and literal
class types.
●​ Syntax: The type of the parameter is explicitly specified:​
C++​
template <typename T, int N> // N is a non-type parameter​
class Array { ... };​

●​ Purpose: Non-type parameters are primarily used when a class needs to fix a size or
value that must be a compile-time constant.
Example using Non-Type Parameter:
The standard library class std::array and std::bitset heavily rely on non-type parameters.

C++

#include <iostream>​

// Class Template with a non-type parameter 'Size'​
template <typename T, int Size> ​
class StaticArray {​
private:​
T arr[Size]; // The size of the array is fixed at compile time​
public:​
void printSize() const {​
std::cout << "Array size: " << Size << std::endl;​
}​
// ... other array-like methods ...​
};​

int main() {​
// Instantiation 1: T=int, Size=10​
StaticArray<int, 10> arr1; ​
[Link](); // Output: Array size: 10​

// Instantiation 2: T=double, Size=5​
StaticArray<double, 5> arr2;​
[Link](); // Output: Array size: 5​

// The compiler generates two completely separate classes: StaticArray<int, 10> and
StaticArray<double, 5>​
return 0;​
}​

8. (a) What is a user defined exception? Write down the scenario


where we require user defined exceptions.

What is a User Defined Exception?

A User Defined Exception (or custom exception) is an exception class created by the
programmer to handle application-specific error conditions that are not covered by the
standard C++ exceptions (like std::bad_alloc or std::out_of_range).
●​ Implementation: Custom exceptions are typically implemented as a class that inherits
from the standard C++ exception base class, std::exception, or one of its derived
classes (like std::runtime_error).
●​ Benefit: By inheriting from std::exception, the custom exception can be caught using a
general catch (const std::exception& e) block and can utilize the standard what()
method to return a descriptive error message.
Example Structure:

C++

#include <exception>​
#include <string>​
class NegativeValueError : public std::exception {​
public:​
const char* what() const noexcept override {​
return "Error: Value cannot be negative.";​
}​
};​
// This custom exception can be thrown and caught like any standard exception.​

Scenario Where We Require User Defined Exceptions

User-defined exceptions are required when the program logic demands specific error states
that provide more context and meaning than a generic exception:
1.​ Handling Business Rule Violations: When an input or operation violates a rule
essential to the application's domain.
○​ Scenario: A bank account class where a withdrawal request exceeds the account
balance. The standard library has no exception for this, so a
InsufficientFundsException is thrown.
2.​ Custom Data Validation: When validating complex data structures or specific ranges.
○​ Scenario: In a date class, a function attempts to set the day of the month to 31 for
February. A InvalidDateException would be required.
3.​ Abstraction of Low-Level Errors: To translate low-level system or library errors into a
high-level, application-specific failure.
○​ Scenario: A network communication class catches a low-level socket error and
re-throws it as a more meaningful NetworkTimeoutException or
ConnectionLostException that the higher-level code can easily understand and
handle.
4.​ Error Categorization: To allow different catch blocks to handle specific errors
differently without having to inspect the error message string.
○​ Scenario: Throwing UserNotFoundException vs. DatabaseConnectionException
allows the program to either prompt the user to register or retry the database
connection.
8. (b) Draw a comparison between Error and Exception and also draw
a comparison between Exceptions and Inheritance.

Comparison between Error and Exception

Feature Error Exception


Definition An Error indicates a serious An Exception indicates a
problem that an application condition that an application
typically cannot recover from. might want to catch and
recover from.
Origin Usually caused by factors Usually caused by faulty
external to the program or program logic or unexpected
fundamental system problems. run-time conditions.
Severity High (often fatal). Medium (can be handled to
prevent program termination).
Recovery Generally impossible or Can be handled using
impractical to handle try-catch blocks, allowing the
gracefully. program to continue
execution.
Examples (C++) Stack overflow, running out of Division by zero, accessing an
memory (often leads to array out of bounds
std::bad_alloc which is (std::out_of_range), trying to
technically an exception but is open a file that doesn't exist.
usually unrecoverable),
hardware failure.

Comparison between Exceptions and Inheritance

Feature Exception Handling Inheritance


Concept A mechanism to manage and A mechanism for creating new
respond to run-time anomalies classes (Derived) from existing
(errors) in a structured manner classes (Base), allowing them
(try, throw, catch). to acquire properties and
behaviors.
Primary Goal Error Management Code Reusability and
(Separating error handling establishing an is-a
code from normal business relationship between classes.
logic).
Relationship The relationship between The relationship between
exception classes is often classes is a core structural
hierarchical (e.g., element of OOP, defining a
std::runtime_error inherits from parent-child dependency (e.g.,
std::exception), which allows a Dog is-a Animal).
base class catch block to
handle derived exceptions.
Keyword(s) try, catch, throw. class, public, protected,
private.
Runtime Role Active only when an error Active at all times; defines the
occurs, causing control flow to structure and methods
jump out of the normal available to the derived class
execution path. object.

You might also like