(A) Synchronous and Asynchronous Exceptions
(A) Synchronous and Asynchronous Exceptions
● Synchronous exceptions occur at a specific point in the program's execution, directly after
the occurrence of a particular event or operation.
● These exceptions are typically caused by errors that arise from the code that is currently
being executed.
For example, if a function tries to divide by zero or access a null pointer, the exception occurs
synchronously as a direct result of the code running at that moment.
Example of Synchronous Exception:
int divide(int a, int b) {
return a / b; // This will throw a division by zero exception if b is 0
}
●
Asynchronous Exceptions:
● They are often harder to handle because they can happen at any time during the program's
execution, not due to a specific operation.
Example of Asynchronous Exception:
○ Signals such as SIGINT (interrupt signal from the keyboard) are asynchronous
exceptions, where the program can be interrupted by a signal at any point in time.
Example:
throw 10; // Throws an integer exception
○
2. Exception Catching:
○ The try block contains code that may throw an exception, and the catch block
handles the exception.
○ The catch block can specify the type of exception it handles (e.g., int,
std::exception).
Example:
try {
throw 10; // Code that throws an exception
} catch (int e) {
cout << "Caught exception: " << e << endl; // Exception handler
}
○
3. Exception Propagation:
○ If an exception is not caught within the current function, it is propagated up the call
stack until it is caught by an appropriate catch block.
○ If no handler is found, the program may terminate or take other actions depending
on the system’s exception handling setup.
Example:
void function1() {
throw 20;
}
void function2() {
function1(); // Exception is propagated from function1 to function2
}
int main() {
try {
function2();
} catch (int e) {
cout << "Caught exception: " << e << endl;
}
return 0;
}
○
4. Exception Handling (Exception Handling Block):
○ The exception is caught, and the corresponding exception handling code is executed.
This block provides the programmer with an opportunity to correct the problem or
take appropriate action (logging the error, cleaning up resources, etc.).
○ If the exception is not handled by the catch blocks in the current scope, it will
propagate to the next higher level, eventually leading to program termination if
unhandled.
Definition A logical error occurs when the A syntactic error occurs when there is a
program runs but mistake in the syntax of the
produces incorrect code, making it impossible
results due to for the compiler to interpret
flawed logic. it.
Detection Logical errors are usually detected Syntactic errors are detected at compile
by the programmer time by the compiler.
after testing, as
they produce
incorrect results.
Program The program will execute but may The program will not compile, and the
provide wrong execution will not start.
results.
Fix Requires revising the logic or Requires fixing the syntax issues such as
algorithms used in punctuation, keywords, or
the code. statement structure.
Summary:
● Synchronous exceptions are errors that occur during the execution of a specific operation,
while asynchronous exceptions are caused by external events, such as interrupts.
● Logical errors arise from flawed program logic and may lead to incorrect output, while
syntactic errors arise from incorrect code structure and prevent the program from compiling.
27. (a) Can a try block be nested in another try block? Give suitable code example to
illustrate your
answer.
(b) When we should go for multiple catch blocks for a single try block?
(c) Write a suitable code that divides a number by zero and show how it behaves when
exception
does not occur and when exception occurs. .
Yes, a try block can be nested inside another try block. This is useful when you have multiple
potential exceptions in different parts of the code, and you want to handle them separately in
different catch blocks.
In such a case, the inner try block can handle exceptions specific to its code, while the outer try
block can catch exceptions that may occur in the inner try block or the code before the inner try
block.
Example:
#include <iostream>
using namespace std;
int main() {
try {
cout << "Outer try block starts" << endl;
try {
cout << "Inner try block starts" << endl;
int a = 10, b = 0;
if (b == 0) {
throw "Division by zero error!";
}
cout << "Result: " << a / b << endl;
} catch (const char* msg) {
cout << "Caught in inner catch block: " << msg << endl;
}
return 0;
}
Explanation:
● The inner try block specifically handles the division by zero, and its associated catch block
catches and handles that exception.
● The exception handling happens in the inner catch block, and after handling it, control
moves to the outer block.
Output:
Outer try block starts
Inner try block starts
Caught in inner catch block: Division by zero error!
Outer try block ends
27. (b) When should we go for multiple catch blocks for a single try block?
Multiple catch blocks should be used for a single try block when:
○ You want to handle different types of exceptions differently. For instance, catching an
int exception separately from a char* exception.
○ When the program can throw multiple types of exceptions, each of which may need
a different recovery strategy or handling logic.
○ If you need specific actions for different types of exceptions, such as cleaning up
resources for certain errors but logging others.
Example:
#include <iostream>
using namespace std;
int main() {
try {
int choice;
cout << "Enter a number: ";
cin >> choice;
if (choice == 1) {
throw 1; // Integer exception
} else if (choice == 2) {
throw "Invalid input"; // String exception
} else {
throw 3.14; // Double exception
}
}
catch (int e) {
cout << "Caught integer exception: " << e << endl;
}
catch (const char* e) {
cout << "Caught string exception: " << e << endl;
}
catch (double e) {
cout << "Caught double exception: " << e << endl;
}
return 0;
}
Explanation:
● Multiple catch blocks are used to catch different types of exceptions (integer, string,
double).
● Each type of exception will be caught by its corresponding catch block, allowing for specific
handling of each type.
Output Example:
If the user enters 1, the output will be:
Caught integer exception: 1
●
27. (c) Write a suitable code that divides a number by zero and show how it
behaves when an exception does not occur and when an exception
occurs.
Code Example:
#include <iostream>
using namespace std;
int main() {
int num1, num2;
cout << "Enter two numbers: ";
cin >> num1 >> num2;
try {
cout << "Trying to divide " << num1 << " by " << num2 << endl;
int result = divide(num1, num2); // Call to divide function
cout << "Result: " << result << endl;
}
catch (const char* msg) {
cout << "Caught exception: " << msg << endl; // Handle exception
}
return 0;
}
Explanation:
● The divide() function checks if the divisor is zero and throws an exception if so.
● In the main() function, the user inputs two numbers, and the program tries to divide the
first by the second.
● If division by zero occurs, it is caught by the catch block, and an error message is displayed.
Behavior:
○ Input: 10 2
Output:
Enter two numbers: 10 2
Trying to divide 10 by 2
Result: 5
○
2. When an exception occurs (division by zero):
○ Input: 10 0
Output:
Enter two numbers: 10 0
Trying to divide 10 by 0
Caught exception: Error: Division by zero
○
Explanation of Behavior:
● When no exception occurs (i.e., num2 != 0), the program executes normally and prints
the result of the division.
● When an exception occurs (i.e., num2 == 0), the throw statement in the divide()
function is executed, and the program enters the catch block to handle the exception and
print the error message.
In C++, catch(...) is a catch-all handler that can catch any type of exception, regardless of its
type. It is used when you want to catch exceptions that are not explicitly specified in other catch
blocks.
Example:
#include <iostream>
using namespace std;
int main() {
try {
throwException(0); // Change this value to test different exceptions
}
catch (const char* msg) {
cout << "Caught a string exception: " << msg << endl;
}
catch (int num) {
cout << "Caught an integer exception: " << num << endl;
}
catch (double num) {
cout << "Caught a double exception: " << num << endl;
}
catch (...) {
cout << "Caught an unknown exception!" << endl;
}
return 0;
}
Explanation:
● The throwException() function throws different types of exceptions based on the input
value.
● The catch blocks handle specific types of exceptions (const char*, int, double).
● The catch(...) block is a catch-all handler that will catch any other type of exception
that is not explicitly handled by the earlier catch blocks.
28. (b) Write a C++ program to implement array-index out of bound exception,
where exception is thrown as object.
In this case, we will throw an exception object to handle out-of-bounds array access.
Example:
#include <iostream>
#include <stdexcept> // For std::out_of_range
using namespace std;
class ArrayIndexOutOfBound {
public:
const char* message;
ArrayIndexOutOfBound(const char* msg) : message(msg) {}
};
int main() {
int arr[] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]);
try {
accessArray(arr, size, 6); // Trying to access an out-of-bounds index
} catch (ArrayIndexOutOfBound& e) {
cout << "Exception: " << e.message << endl;
}
return 0;
}
Explanation:
● The accessArray function checks if the provided index is within bounds; if not, it throws
an ArrayIndexOutOfBound exception object.
● The main() function handles this exception and prints the error message.
Output:
Exception: Array index out of bounds!
28. (c) Create two custom exception or user-defined exception classes viz. TooHot
and TooCold. Write a program and throw TooHot if the temperature
exceeds 40 degrees and throw TooCold if the temperature is less than
20 degrees.
Example:
#include <iostream>
using namespace std;
class TooCold {
public:
const char* message;
TooCold(const char* msg) : message(msg) {}
};
try {
checkTemperature(temperature);
} catch (TooHot& e) {
cout << "Caught exception: " << e.message << endl;
} catch (TooCold& e) {
cout << "Caught exception: " << e.message << endl;
}
return 0;
}
Explanation:
● We define two custom exception classes TooHot and TooCold to handle extreme
temperature conditions.
Output:
If the input temperature is 45:
Enter the temperature: 45
Caught exception: Temperature is too hot! Exceeds 40 degrees.
1.
2.
3.
Summary:
● (a) catch(...) is a generic catch block that can catch any type of exception, including
ones that do not have explicit catch handlers.
● (b) In the array out-of-bound example, an exception object is thrown when accessing an
invalid index in the array.
● (c) In the temperature exception program, two custom exceptions (TooHot and TooCold)
are created to handle extreme temperature conditions.
Templates in C++ provide a way to write generic functions and classes. Templates allow you to define
functions or classes that work with any data type. When a template is used, the C++ compiler
generates the appropriate code for the data type provided.
● A function template defines a generic function that can accept arguments of any data type.
● A class template defines a generic class that can handle any data type.
● The compiler creates specialized versions of these templates when they are used with a
specific type.
Example:
#include <iostream>
using namespace std;
int main() {
cout << add(3, 5) << endl; // Works with integers
cout << add(2.5, 3.1) << endl; // Works with doubles
return 0;
}
● template <typename T> defines a template that works with any data type T.
● When add(3, 5) is called, the compiler creates a version of the add function for int.
● When add(2.5, 3.1) is called, the compiler creates a version of the add function for
double.
● template <typename T>: This declares a template where T is the placeholder for any
data type.
● ParameterType: The type of the function's parameters, which can be any data type.
Explanation:
● The function compares the two arguments and returns the larger one.
● The template is instantiated by the compiler at compile time for different types when the
function is called with those types.
Output:
Max of 3 and 5: 5
Max of 2.5 and 3.1: 3.1
Max of 'A' and 'B': B
29. (c) Write a function template for finding the largest number. The array
parameters must be of generic data types.
Function Template for Finding the Largest Number in an Array:
#include <iostream>
using namespace std;
int main() {
int arrInt[] = {3, 5, 1, 8, 2};
double arrDouble[] = {3.1, 2.5, 4.6, 7.9, 0.3};
return 0;
}
Explanation:
● template <typename T>: The template allows the function to work with any data type
T.
● It initializes largest as the first element of the array and iterates through the array to find
the largest element.
Output:
Largest integer: 8
Largest double: 7.9
Summary:
● (a) Generic programming is a paradigm where code is written independently of specific data
types, and templates in C++ allow this by defining generic functions and classes that work
with any data type.
● (b) A function template is a template that defines a function that can be used with any data
type, and its syntax involves specifying template <typename T> before the function
definition.
● (c) The provided function template findLargest() works with arrays of any data type
and returns the largest number in the array.
30. (a) What is class template? Write a class template to implement a calculator. In this
program write one member function definition outside the class template.
(b) What do you mean by overloaded function template? Give an example C++ code of
overloaded function template.
30. (a) What is a class template? Write a class template to implement a calculator.
In this program write one member function definition outside the
class template.
A class template in C++ allows us to create a generic class that can work with any data type. This way,
the same class can be used to handle different types of data, such as int, float, or double,
without having to write separate classes for each type.
int main() {
Calculator<int> calcInt;
Calculator<float> calcFloat;
return 0;
}
Explanation:
● template <typename T>: Defines the class template for the class Calculator.
● The add, subtract, multiply, and divide functions are defined inside the class
template.
● Member function definitions are provided outside the class template using the template
<typename T> prefix, and Calculator<T>::function_name syntax.
● In main(), objects of the Calculator class are created for int and float types and
used to perform various operations.
Output:
Integer addition: 30
Integer subtraction: 5
Float addition: 30.8
Float division: 4.2
30. (b) What do you mean by overloaded function template? Give an example C++
code of overloaded function template.
An overloaded function template is a scenario where multiple function templates with the same
name are defined, but they accept different types or numbers of arguments. This allows the same
function name to work with different types, making the code more readable and flexible.
int main() {
cout << "Single argument: " << display(10) << endl; // For single argument
cout << "Two arguments: " << display(10, 20) << endl; // For two arguments
cout << "Single argument (double): " << display(10.5) << endl; // For single argument (double)
cout << "Two arguments (double): " << display(10.5, 20.5) << endl; // For two arguments (double)
return 0;
}
Explanation:
● Both functions are function templates, and the type T is determined when the function is
called.
● The correct overloaded version of the function is chosen based on the number of arguments
passed at compile time.
Output:
Single argument: 10
Two arguments: 30
Single argument (double): 10.5
Two arguments (double): 31
Key Points:
● Function Overloading: Allows multiple functions with the same name but different
signatures (number or types of arguments).
● Template Overloading: Involves the use of overloaded template functions to work with
different data types or different numbers of arguments. This enables greater flexibility and
reuse of code.
Summary:
● (a) A class template is a blueprint for creating classes that can work with any data type. We
implemented a Calculator class template that performs operations like addition, subtraction,
multiplication, and division. Member function definitions were written outside the class
template.
● (b) An overloaded function template allows multiple versions of the same function name to
exist, with each version handling different arguments. We demonstrated function template
overloading by defining multiple versions of the display function to handle different
numbers of arguments.
31. (a) How is namespace is different from a class? Why do we use namespace in our
program?
(b) Create a namespace Employee, in which EmpId and EmpName are the two classes.
EmpId class
has emp_id data member and show() function, Name class has name data member
and
corresponding show() function. Create object for both classes.
(c) What is the difference between Template and Macro?
31. (a) How is namespace different from a class? Why do we use namespace in our
program?
Difference between Namespace and Class:
1. Purpose:
○ Class: A class is a blueprint for creating objects, encapsulating data for the object
(member variables) and methods that operate on the data (member functions). It is
a fundamental building block of object-oriented programming.
2. Members:
○ Namespace: Can hold functions, variables, constants, classes, and other
namespaces. It does not have constructors or destructors.
○ Class: Can have member functions, member variables, constructors, destructors, and
operators.
3. Instantiation:
○ Namespace: Does not have access control modifiers (like public, private,
protected). All members are accessible.
○ Class: Has access control (public, private, protected) to manage visibility of data
members and functions.
● Avoid name conflicts: Namespaces help prevent name clashes, especially when working with
large codebases or multiple libraries. You can encapsulate code into logical groups.
● Code organization: It helps organize functions, classes, and other entities in a modular way.
● Code readability: By using namespaces, you can group related code together, improving
readability and maintainability.
31. (b) Create a namespace Employee, in which EmpId and EmpName are the two
classes. EmpId class has emp_id data member and show()
function, and EmpName class has name data member and
corresponding show() function. Create objects for both classes.
#include <iostream>
using namespace std;
namespace Employee {
class EmpId {
public:
int emp_id;
// Constructor
EmpId(int id) : emp_id(id) {}
class EmpName {
public:
string name;
// Constructor
EmpName(string n) : name(n) {}
int main() {
// Creating objects of EmpId and EmpName classes inside Employee namespace
Employee::EmpId emp1(101);
Employee::EmpName emp2("John Doe");
return 0;
}
Explanation:
● We defined a namespace Employee that contains two classes: EmpId and EmpName.
○ EmpId class has a data member emp_id and a method show() that prints the
employee's ID.
○ EmpName class has a data member name and a method show() that prints the
employee's name.
● In main(), we created objects emp1 of class EmpId and emp2 of class EmpName, and
displayed their information using their respective show() methods.
Output:
Employee ID: 101
Employee Name: John Doe
31. (c) What is the difference between Template and Macro?
Aspect Template Macro
Definition Templates are a feature in C++ that allows Macros are preprocessor directives
writing generic and in C and C++ that
reusable code that works define code
with any data type. They snippets to be
are part of the language. replaced during
compilation.
Type Safety Templates are type-safe. The compiler Macros are not type-safe; they
checks the types at perform textual
compile-time. substitution
without type
checking.
Functionality Templates allow creating functions and Macros perform simple text
classes that can work with substitution and
any data type. can be used for
constants or code
snippets.
Scope Templates have scope and are subject to Macros do not have scope and are
C++'s scoping rules. replaced globally
throughout the
code.
Debugging Templates are easier to debug, and errors Macros can be difficult to debug
are caught during because errors are
compilation. often hard to trace
due to text
substitution.
Example of Template:
template <typename T>
T add(T a, T b) {
return a + b;
}
Example of Macro:
#define ADD(a, b) ((a) + (b))
Key Differences:
● Type Safety: Templates ensure type safety, whereas macros can lead to unintended behavior
since they do not check types.
● Compile-Time vs Preprocessor: Templates are evaluated by the compiler, while macros are
handled by the preprocessor before compilation starts.
● Error Handling: Template errors are caught by the compiler, making them easier to debug.
Macro errors are harder to debug because they are replaced by text before compilation.
#include <iostream>
using namespace std;
int main() {
// Accessing functions from both namespaces
Employee::display(); // Calls the display function in Employee namespace
Department::display(); // Calls the display function in Department namespace
return 0;
}
Explanation:
● Employee and Department are two separate namespaces.
● Both namespaces contain a function display(), but they perform different tasks
(displaying information about the employee and department).
● In main(), we access both display() functions using the scope resolution operator (::)
to specify which namespace we are referring to.
Output:
Employee Information
Department Information
32. (b) What is a nested namespace? Write its syntax. Write a C++ code to
demonstrate nested namespace.
Definition of Nested Namespace:
A nested namespace is a namespace defined inside another namespace. It allows organizing code in
multiple layers, providing a more granular level of namespace encapsulation.
Syntax:
namespace Outer {
namespace Inner {
// Declarations or definitions inside the inner namespace
}
}
You can access members of a nested namespace by using the scope resolution operator for both the
outer and inner namespaces.
int main() {
// Accessing the function inside the inner namespace using the scope resolution operator
Outer::Inner::show(); // Calling the show function inside the inner namespace
return 0;
}
Explanation:
● Outer is the outer namespace, and Inner is the nested namespace inside it.
● In main(), we access the show() function by specifying both the outer and inner
namespaces using the scope resolution operator (::).
Output:
Inside the Inner Namespace
From C++17, you can use the namespace keyword to directly define nested namespaces in a more
concise way:
#include <iostream>
using namespace std;
int main() {
Outer::Inner::show(); // Calling the show function inside the nested namespace
return 0;
}
This syntax is equivalent to the previous version, but it provides a cleaner and more compact way to
define nested namespaces.
33. (a) What is the output of the following code snippet?. If you think it is incorrect, rectify
the problem and explain it.
Derived Class Constructor Syntax: Object Slicing (gpt)
(b) Write a Father class with data member f_age, and Son class with data member
s_age. If f_age and s_age is a wrong value (say f_age<0, s_age >f_age) then Father
and Son class constructor will throw exception as object. Both of them have int
getAge() function to return their respective age. Implement runtime polymorphism
to implement this problem.
To solve this problem, we will define two classes: Father and Son. The Father class will have a
data member f_age, and the Son class will have a data member s_age. Both classes will have
their constructors throw exceptions if the values for f_age or s_age are invalid. Additionally, both
classes will have a getAge() function to return their respective ages.
Steps:
1. Exception Handling: The constructors of both classes will throw an exception if the age
values are invalid (i.e., if f_age < 0 or if s_age > f_age).
2. Runtime Polymorphism: We will implement runtime polymorphism using a virtual function
getAge() that is overridden in the derived Son class.
3. Base Class and Derived Class: The Father class will be the base class, and the Son class
will be the derived class.
4. Exception Propagation: We will use the try-catch blocks to catch exceptions in the main
function.
Code Implementation:
#include <iostream>
#include <stdexcept>
using namespace std;
class Father {
protected:
int f_age;
public:
// Constructor of Father class
Father(int age) {
if (age < 0) {
throw runtime_error("Father's age cannot be negative");
}
f_age = age;
cout << "Father's age set to: " << f_age << endl;
}
// Virtual destructor
virtual ~Father() {
cout << "Father's object destroyed" << endl;
}
};
public:
// Constructor of Son class
Son(int fatherAge, int sonAge) : Father(fatherAge) {
if (sonAge < 0) {
throw runtime_error("Son's age cannot be negative");
}
if (sonAge > fatherAge) {
throw runtime_error("Son's age cannot be greater than Father's age");
}
s_age = sonAge;
cout << "Son's age set to: " << s_age << endl;
}
int main() {
try {
// Creating Father and Son objects with invalid values to trigger exceptions
Father* father = new Father(50); // Valid Father
Son* son = new Son(50, 25); // Valid Son
try {
// Attempting to create invalid Son and Father objects
Father* invalidFather = new Father(-5); // Invalid Father's age
Son* invalidSon = new Son(50, 60); // Invalid Son's age
} catch (const exception& e) {
cout << "Exception caught: " << e.what() << endl;
}
return 0;
}
Explanation:
1. Father Class:
○ Father(int age) constructor checks if the age is valid (age must not be
negative). If invalid, it throws a runtime_error exception.
○ Virtual destructor ensures proper cleanup when the object is destroyed (important
for runtime polymorphism).
○ The Son constructor checks that s_age is not negative and that the son's age is less
than or equal to the father's age.
4. Polymorphism:
○ In the main() function, when the base class Father pointer is used to call the
getAge() function, the derived class Son's version of the getAge() function is
invoked due to runtime polymorphism (because getAge() is a virtual function).
Sample Output:
Father's age set to: 50
Son's age set to: 25
Father's age: 50
Son's age: 25
Father's object destroyed
Son's object destroyed
Exception caught: Father's age cannot be negative
Exception caught: Son's age cannot be greater than Father's age
Key Points:
● Runtime Polymorphism: The function getAge() in the base class Father is declared as
virtual, allowing it to be overridden in the derived class Son. The correct function is
called at runtime based on the type of object.
● Exception Handling: The constructors of both Father and Son throw exceptions when
invalid data is provided, and we handle these exceptions using try-catch.
● Inheritance and Virtual Functions: Son inherits from Father, and both classes have virtual
functions (getAge()), enabling runtime polymorphism.