Solved[December 2023] BCS031 - Programming in C++

Hey there! Welcome to KnowledgeKnot! Don't forget to share this with your friends and revisit often. Your support motivates us to create more content in the future. Thanks for being awesome!

Question 1(a). What is inheritance? Explain three different types of inheritance. (5 Marks)

Answer:

Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a new class (derived class) to inherit properties and behaviors (data members and member functions) from an existing class (base class). This promotes code reusability and establishes a hierarchical relationship between classes.

Types of Inheritance:

  1. Single Inheritance: In this type of inheritance, a derived class inherits from only one base class. This is the simplest form of inheritance where the derived class extends the functionality of one base class.
  2. Multiple Inheritance: In this type of inheritance, a derived class inherits from more than one base class. This allows the derived class to combine and utilize the functionalities of multiple base classes, though it can introduce complexity such as ambiguity in the inherited attributes or methods.
  3. Multilevel Inheritance: In this type of inheritance, a class is derived from another derived class, forming a chain of inheritance. This creates a hierarchy where a class inherits from a base class, and another class inherits from this derived class, extending the inheritance chain.

Question 1(b). What is an exception? How is exception handled in C++? Explain with an example. (5 Marks)

Answer:

An exception in C++ is an unexpected or erroneous event that occurs during the execution of a program, disrupting the normal flow of control. Exceptions can arise from various sources, such as errors in input data, runtime errors like division by zero, or resource unavailability.

Handling Exceptions: Exception handling in C++ involves the use of three keywords: try, catch, and throw.

  1. try block: This block contains the code where an exception might occur. It is followed by one or more catch blocks to handle exceptions.
  2. catch block: This block catches and handles exceptions thrown within the associated try block. Each catch block specifies the type of exception it can handle.
  3. throw statement: This statement is used to manually throw an exception within a try block. It typically includes an object of a class type derived from std::exception or one of its subclasses.

Code Example:-



double division(int a, int b) {
    if (b == 0) {
        throw "Division by zero exception";
    }
    return static_cast<double>(a) / b;
}

int main() {
    int x = 10, y = 0;
    try {
        double result = division(x, y);
        cout << "Result of division: " << result << endl;
    } catch (const char* msg) {
        cerr << "Exception caught: " << msg << endl;
    }
    return 0;
}

Question 1(c). What is operator overloading? Explain its advantages. (5 Marks)

Answer:

Operator overloading is a feature in C++ that allows operators to be redefined for user-defined data types. It enables programmers to define custom behaviors for operators when they are applied to objects of a class. For example, operators like +, -, *, /, etc., can be overloaded to work with user-defined types.

Advantages of Operator Overloading:

  • Natural Syntax: Operator overloading allows programmers to use familiar operators with user-defined types, making code more intuitive and readable. For example, vector1 + vector2 instead of vector1.add(vector2).
  • Code Clarity: Overloading operators can lead to more concise and expressive code, especially for mathematical or data manipulation operations. This can improve the clarity and maintainability of the codebase.
  • Customized Behavior: By overloading operators, developers can define specific behaviors for operations based on the requirements of the class. This allows for tailored functionality that aligns with the semantics of the class.
  • Integration with Standard Library: Overloaded operators can seamlessly integrate with standard library functionality, providing consistency and compatibility with existing codebases and programming practices.
  • Improved Efficiency: Well-designed operator overloads can lead to more efficient code execution compared to function calls, especially for operations involving complex data structures or algorithms.
  • Question 1(d). Explain the process of memory deallocation in C++ with the help of an example. (5 Marks)

    Answer:

    Memory deallocation in C++ is the process of releasing dynamically allocated memory when it is no longer needed, preventing memory leaks and ensuring efficient memory usage. In C++, memory deallocation is typically performed using the delete operator for single objects and delete[] for arrays.

    Process of Memory Deallocation:

    1. Allocate Memory: Dynamically allocate memory using operators like new or new[]. This memory allocation occurs on the heap.
    2. Use Memory: Utilize the allocated memory for storing data or objects as needed during program execution.
    3. Release Memory: Once the allocated memory is no longer required, explicitly deallocate it using the delete or delete[] operators.

    Code Example -

    
    
    int main() {
        // Allocate memory for a single integer
        int* ptr = new int;
    
        // Assign value to the allocated memory
        *ptr = 10;
    
        // Display the value
        cout << "Value stored at memory location: " << *ptr << endl;
    
        // Deallocate the memory
        delete ptr;
    
        // Attempting to access the deallocated memory may result in undefined behavior
        // cout << "Value stored at memory location after deallocation: " << *ptr << endl;
    
        return 0;
    }
    

    Question 1(e). Explain the working of GOTO statement. Does its usage violate the rules of OOP? Explain. (5 Marks)

    Answer:

    The goto statement in C++ is a control statement that allows for an unconditional jump to another line within the same function. It uses a label to mark the destination of the jump.

    Working of GOTO Statement:
    Label Definition: A label is defined by an identifier followed by a colon (:).
    Goto Statement: The goto statement is followed by the label name to which control should jump.

    Yes, the use of goto generally violates the principles of object-oriented programming (OOP) for several reasons:

    • Spaghetti Code: goto can make the code difficult to read and maintain. It can lead to "spaghetti code" where the flow of control jumps around unpredictably, making it hard to follow the logic.
    • Encapsulation: OOP principles emphasize encapsulation, where data and methods are bundled together. goto breaks this encapsulation by introducing jumps that can bypass the structured flow of method calls and control statements.
    • Modularity: OOP promotes modularity, where code is divided into distinct, self-contained modules or classes. goto undermines modularity by creating non-local jumps that can disrupt the structure and independence of these modules.
    • Maintainability: Code that uses goto is generally harder to debug and maintain, as it obscures the normal flow of control. This makes it difficult to reason about the state of the program at any given point.

    Question 1(f). Explain the use of List container with the help of a program. (5 Marks)

    Answer:

    The list container in C++ is part of the Standard Template Library (STL). It is a sequence container that allows non-contiguous memory allocation, making it an ideal choice for frequent insertions and deletions. Unlike arrays or vectors, lists are implemented as doubly-linked lists, providing efficient insertion and deletion operations at both ends and in the middle.

    Features of list Container:

    • Allows bidirectional iteration.
    • Efficient insertion and deletion at any position.
    • Does not provide direct access by index.

    Program Example:-

    #include <iostream>
    #include <list>
    using namespace std;
    
    int main() {
        // Declare a list of integers
        list<int> myList;
    
        // Insert elements into the list
        myList.push_back(10);  // Add 10 at the end
        myList.push_back(20);  // Add 20 at the end
        myList.push_front(5);  // Add 5 at the beginning
    
        // Display elements of the list
        cout << "List elements: ";
        for (int value : myList) {
            cout << value << " ";
        }
        cout << endl;
    
        // Insert an element in the middle
        list<int>::iterator it = myList.begin();
        advance(it, 2);  // Move iterator to the third position
        myList.insert(it, 15);  // Insert 15 before the third element
    
        // Display elements after insertion
        cout << "List elements after insertion: ";
        for (int value : myList) {
            cout << value << " ";
        }
        cout << endl;
    
        // Remove an element from the list
        myList.remove(10);  // Remove all occurrences of 10
    
        // Display elements after removal
        cout << "List elements after removal: ";
        for (int value : myList) {
            cout << value << " ";
        }
        cout << endl;
    
        return 0;
    }
    

    Question 1(g). Explain the role of destructor in C++ programming with the help of a program. (5 Marks)

    Answer:

    A destructor in C++ is a special member function that is automatically called when an object goes out of scope or is explicitly deleted. The main purpose of a destructor is to release resources that the object may have acquired during its lifetime, such as memory or file handles, ensuring proper cleanup and preventing resource leaks.

    Key Characteristics of Destructors:
    • Same Name as the Class: Prefixed with a tilde (~).
    • No Parameters: Destructors cannot have parameters.
    • No Return Type: Destructors do not return a value.
    • Automatically Called: Automatically invoked when the object goes out of scope or is deleted.

    Code Example :-

    
    
    class MyClass {
    private:
        int* data;
    
    public:
        // Constructor
        MyClass(int size) {
            data = new int[size];  // Allocate memory
            cout << "Constructor: Memory allocated." << endl;
        }
    
        // Destructor
        ~MyClass() {
            delete[] data;  // Release memory
            cout << "Destructor: Memory deallocated." << endl;
        }
    };
    
    int main() {
        MyClass obj(5);  // Creating an object
        // Destructor will be called automatically here
        return 0;
    }
    

    Question 1(h). Briefly describe rules of operator overloading in C++. (5 Marks)

    Answer:

    Rule for operator overloading in C++:

    • Syntax and Declaration: Use operator keyword followed by the operator symbol. Example: class Complex Complex operator+(const Complex& other); ;
    • Operand Requirements: At least one operand must be a user-defined type (class or struct).
    • Unchangeable Operators: Operators that cannot be overloaded: ::, ., .*, ?:, sizeof, typeid, alignof, noexcept, decltype.
    • Preserve Operator Characteristics: Operators maintain their original precedence and associativity. Unary operators remain unary; binary operators remain binary.
    • Member vs. Non-Member Functions: Unary operators are typically member functions. Binary operators can be member or non-member functions (non-member often as friends).
    • Assignment and Special Operators: Assignment (=), function call (()), and subscript ([]) must be member functions. Example: class Array int& operator[](int index); ;
    • Return Type: Can be any type, often references or const objects for chaining.
    • Consistency: Overloaded operators should mimic the behavior of built-in operators to avoid confusion.
    • Inherited Operators: Derived classes inherit overloaded operators from base classes unless explicitly overridden.
    • Overloading Stream Operators: << and >> are commonly overloaded as non-member functions for input/output operations. Example: ostream& operator<<(ostream& os, const Complex& c);

    Question 2(a). Differentiate between abstraction and encapsulation with an example. (5 Marks)

    Answer:

    Abstraction - Abstraction simplifies complex systems by hiding unnecessary details and showing only essential features, allowing users to interact with high-level concepts without needing to understand the underlying implementation. For instance, a TV remote control abstracts the process of changing channels or adjusting volume, providing buttons for these actions without revealing the internal circuitry.

    Encapsulation - Encapsulation bundles data and methods into a single unit and restricts access to certain components. This technique protects the internal state of an object from external interference, ensuring data integrity and preventing misuse. For example, a capsule toy vending machine encapsulates the toys inside, allowing users to insert coins and turn a knob to receive a toy without accessing or damaging the internal mechanism.

    Code Example :-

    
    
    // Abstraction: TV Remote Control
    class TVRemoteControl {
    public:
        void powerOn() {
            cout << "TV powered on." << endl;
        }
    
        void changeChannel(int channel) {
            cout << "Channel changed to " << channel << endl;
        }
    
        void adjustVolume(int volume) {
            cout << "Volume adjusted to " << volume << endl;
        }
    };
    
    // Encapsulation: Capsule Toy Vending Machine
    class ToyVendingMachine {
    public:
        void insertCoin() {
            cout << "Coin inserted." << endl;
        }
    
        void turnKnob() {
            cout << "Knob turned. Toy dispensed." << endl;
        }
    
    private:
        // Internal mechanism and toys encapsulated
    };
    
    int main() {
        // Abstraction: Using TV Remote Control
        TVRemoteControl remote;
        remote.powerOn();
        remote.changeChannel(5);
        remote.adjustVolume(20);
    
        // Encapsulation: Using Toy Vending Machine
        ToyVendingMachine machine;
        machine.insertCoin();
        machine.turnKnob();
    
        return 0;
    }
    

    Question 2(b). Write a C++ program to find whether a given number is odd or even. (5 Marks)

    Answer:

    Code Example :-

    
    
    int main() {
        int number;
    
        // Input from user
        cout << "Enter a number: ";
        cin >> number;
    
        // Check if number is even or odd
        if (number % 2 == 0) {
            cout << number << " is even." << endl;
        } else {
            cout << number << " is odd." << endl;
        }
    
        return 0;
    }
    

    Question 2(c). What is access specifier in C++? Briefly explain its different types. (5 Marks)

    Answer:

    In C++, access specifiers are keywords used to control the visibility and accessibility of class members (data members and member functions) from outside the class. They determine which parts of a class are accessible to other classes or functions and help in implementing the concept of encapsulation.

    Different Types of Access Specifiers:

    1. Public: Members declared as public are accessible from anywhere, both within the class and outside the class. Public members can be accessed by objects of the class, derived classes, and external functions. Example: Public member functions for accessing and modifying class data.
    2. Private:Members declared as private are accessible only within the class in which they are declared. Private members cannot be accessed directly by objects of the class or by derived classes.Example: Private data members containing sensitive information that should not be directly modified from outside the class.
    3. Protected:Members declared as protected are accessible within the class and its derived classes. Protected members cannot be accessed by objects of the class or external functions but can be accessed by derived classes. Example: Protected member functions or data members that should be accessible only to derived classes for inheritance.

    Question 2(d). What is constructor overloading? Explain with the help of an example. (5 Marks)

    Answer:

    Constructor overloading is a feature in C++ that allows a class to have multiple constructors with different parameter lists. Each constructor can initialize the object in a different way, providing flexibility and convenience to the user when creating objects of the class.

    
    
    class Rectangle {
    private:
        int width;
        int height;
    
    public:
        // Default Constructor
        Rectangle() {
            width = 0;
            height = 0;
        }
    
        // Parameterized Constructor with one parameter
        Rectangle(int side) {
            width = side;
            height = side;
        }
    
        // Parameterized Constructor with two parameters
        Rectangle(int w, int h) {
            width = w;
            height = h;
        }
    
        // Method to display the dimensions of the rectangle
        void displayDimensions() {
            cout << "Width: " << width << ", Height: " << height << endl;
        }
    };
    
    int main() {
        // Creating objects using different constructors
        Rectangle defaultRect; // Using the default constructor
        Rectangle square(5);   // Using parameterized constructor for square
        Rectangle customRect(10, 20); // Using parameterized constructor for custom rectangle
    
        // Displaying dimensions of each rectangle
        cout << "Default Rectangle:" << endl;
        defaultRect.displayDimensions();
    
        cout << "Square:" << endl;
        square.displayDimensions();
    
        cout << "Custom Rectangle:" << endl;
        customRect.displayDimensions();
    
        return 0;
    }
    

    Question 3(a). What is the concept of reusability? Explain how this concept is implemented in C++. (5 Marks)

    Answer:

    The concept of reusability in programming refers to the ability to use existing code components (such as classes, functions, or modules) in multiple contexts or applications without modification. Reusability promotes efficiency, reduces redundancy, and enhances maintainability by leveraging existing solutions rather than reinventing the wheel.

    Implementation of Reusability in C++:
    In C++, reusability is primarily achieved through the following mechanisms:

    1. Classes and Objects: Classes encapsulate data and behavior into a single unit, providing a blueprint for creating objects. Objects are instances of classes and can be reused in various parts of a program or in different programs altogether. Class hierarchies and inheritance facilitate code reuse by allowing derived classes to inherit properties and behavior from base classes.
    2. Function Libraries: C++ supports the creation of function libraries, which are collections of related functions grouped together for reuse. These libraries can be shared across multiple projects or distributed as part of reusable software components. Standard Template Library (STL) provides a rich set of generic algorithms and data structures that promote code reuse and interoperability.
    3. Templates: Templates enable generic programming in C++, allowing algorithms and data structures to be defined independently of the data types they operate on. Template classes and functions can be instantiated with different data types, promoting code reuse without sacrificing type safety or efficiency.
    4. Header Files: Header files (.h files) contain declarations of classes, functions, and variables that can be shared across multiple source files. By including header files in different parts of a program, developers can reuse declarations and definitions without duplicating code.

    Question 3(b). What is multiple inheritance? What are its drawbacks? Explain with the help of an example. (5 Marks)

    Answer:

    Multiple inheritance is a feature in object-oriented programming languages like C++ that allows a class to inherit properties and behavior from multiple base classes. In other words, a derived class can have more than one parent class, and it inherits members from all of them.

    Drawbacks of Multiple Inheritance:

    • Diamond Problem: Occurs when a class inherits from two or more classes with a common base, leading to ambiguity in member access and method resolution.
    • Complexity and Confusion: Increases the complexity of the class hierarchy, making it difficult to understand and maintain. Managing relationships between multiple base classes can be challenging.
    • Name Clashes and Ambiguity: Inherited members with the same name from different base classes can lead to conflicts and require explicit qualification, resulting in verbose code.
    • Increased Coupling: Enhances coupling between classes, making the code more tightly coupled and less modular. Changes in one base class may affect derived classes, leading to the fragile base class problem.

    Code Example -

    
    // Base class 1
    class Animal {
    public:
        void speak() {
            cout << "Animal speaks." << endl;
        }
    };
    
    // Base class 2
    class Bird {
    public:
        void fly() {
            cout << "Bird flies." << endl;
        }
    };
    
    // Derived class inheriting from Animal and Bird
    class Parrot : public Animal, public Bird {
    public:
        void chirp() {
            cout << "Parrot chirps." << endl;
        }
    };
    
    int main() {
        Parrot parrot;
    
        // Accessing methods from base classes
        parrot.speak(); // Ambiguity: which speak() method to call?
        parrot.fly();   // Ambiguity: which fly() method to call?
        parrot.chirp();
    
        return 0;
    }
    

    Question 3(c). For classes in inheritance hierarchy, discuss the order of constructor and destructor call with the help of a program. (5 Marks)

    Answer:

    #include <iostream>
    using namespace std;
    
    // Base class
    class Base {
    public:
        // Constructor
        Base() {
            cout << "Base Constructor called." << endl;
        }
    
        // Destructor
        ~Base() {
            cout << "Base Destructor called." << endl;
        }
    };
    
    // Derived class
    class Derived : public Base {
    public:
        // Constructor
        Derived() {
            cout << "Derived Constructor called." << endl;
        }
    
        // Destructor
        ~Derived() {
            cout << "Derived Destructor called." << endl;
        }
    };
    
    int main() {
        cout << "Creating Base object:" << endl;
        Base baseObj;
        cout << endl;
    
        cout << "Creating Derived object:" << endl;
        Derived derivedObj;
        cout << endl;
    
        return 0;
    }
    

    Question 3(d). What is a friend function? Explain the advantage of friend function. (5 Marks)

    Answer:

    A friend function in C++ is a function that is not a member of a class but has access to the private and protected members of that class. To declare a function as a friend of a class, the keyword friend is used in the class declaration.

    Advantages of Friend Function:

    • Access to Private and Protected Members: Friend functions can access private and protected members of a class, enabling manipulation or inspection of object states.
    • Improved Encapsulation: Friend functions offer controlled access to private members, enhancing encapsulation by restricting access to selected functions rather than making all members public.
    • Flexibility in Design: They facilitate implementation of operations related to a class but not part of its interface, promoting modular design and grouping logically related operations.
    • Efficiency and Performance: Friend functions enhance efficiency by enabling direct access to class members, beneficial in performance-critical applications where access time is crucial.

    Question 4(a). Explain the term polymorphism. Explain the types of polymorphism supported by C++. (5 Marks)

    Answer:

    Polymorphism, in the context of object-oriented programming, refers to the ability of objects to exhibit different behaviors or take on multiple forms based on their context or the messages they receive. It allows objects of different classes to be treated uniformly through a common interface, facilitating code reuse, flexibility, and extensibility.

    Types of Polymorphism Supported by C++:

    • Compile-time Polymorphism (Static Binding): Also known as early binding, compile-time polymorphism is resolved at compile time. It includes:
      • Function Overloading: Where multiple functions with the same name but different parameter lists can exist in the same scope.
      • Operator Overloading: Where operators can be overloaded to work with user-defined types.
    • Run-time Polymorphism (Dynamic Binding): Also known as late binding, run-time polymorphism is resolved at runtime. It includes:
      • Virtual Functions and Function Overriding: Base class pointers or references can be used to invoke derived class methods, allowing for dynamic method dispatch.
      • Abstract Classes and Pure Virtual Functions: Abstract classes define interfaces through pure virtual functions, and concrete derived classes provide implementations for these functions.

    Question 4(b). What is function overriding? With the help of a program, illustrate the concept of function overloading. (5 Marks)

    Answer:

    Function overriding is a feature in object-oriented programming that allows a derived class to provide a specific implementation of a function that is already defined in its base class. This allows the derived class to customize the behavior of the inherited function without changing its signature.

    // Base class
    class Animal {
    public:
        // Virtual function
        virtual void makeSound() {
            cout << "Animal makes a generic sound." << endl;
        }
    };
    
    // Derived class
    class Dog : public Animal {
    public:
        // Overridden virtual function
        void makeSound() override {
            cout << "Dog barks: Woof! Woof!" << endl;
        }
    };
    
    // Derived class
    class Cat : public Animal {
    public:
        // Overridden virtual function
        void makeSound() override {
            cout << "Cat meows: Meow! Meow!" << endl;
        }
    };
    

    Question 4(c). What is an abstract class? How is an abstract class implemented in C++? Explain with the help of a program. (5 Marks)

    Answer:

    An abstract class in C++ is a class that cannot be instantiated directly and may contain one or more pure virtual functions. Pure virtual functions are virtual functions declared in the base class but have no implementation. Abstract classes serve as interfaces or blueprints for derived classes, defining a common interface that derived classes must implement.

    class Shape {
    public:
        // Pure virtual function
        virtual void draw() = 0;
    
        // Concrete function
        void displayInfo() {
            cout << "This is a shape." << endl;
        }
    };
    
    // Derived class
    class Circle : public Shape {
    public:
        // Override pure virtual function
        void draw() override {
            cout << "Drawing a circle." << endl;
        }
    };
    
    // Derived class
    class Rectangle : public Shape {
    public:
        // Override pure virtual function
        void draw() override {
            cout << "Drawing a rectangle." << endl;
        }
    };
    

    Question 4(d). What is the purpose of the static keyword in C++? What happens when it is appended before a variable in C++? Explain. (5 Marks)

    Answer:

    In C++, the static keyword serves various purposes depending on where it is used:

    • Static Variables: When used with variables inside a function or a class, it signifies that the variable retains its value between function or method calls or exists independently of any object of the class.
    • Static Functions: When used with functions inside a class, it signifies that the function is independent of any particular object of the class and can be called using the class name without creating an instance of the class.
    • Static Data Members: When used with data members inside a class, it signifies that the data member is shared by all objects of the class. Only one copy of the data member exists regardless of how many objects of the class are created.

    Behavior of Static Variables:

    When the static keyword appended before variables behave differently:
    Local static variables: Initialized once before the program starts, keeps its value between function calls, stored in program memory.
    Static data members in a class: Shared among all class objects, initialized once before any object creation, modifications affect all objects.

    Question 5(a). Explain the use of the do-while loop with the help of a program. (5 Marks)

    Answer:

    The do-while loop in C++ is used to execute a block of code at least once and then repeatedly execute the block as long as a specified condition is true. The main difference between a do-while loop and a while loop is that the do-while loop guarantees that the code block will be executed at least once, regardless of whether the condition is initially true.

    Example Program :-

    #include <iostream>
    using namespace std;
    
    int main() {
        int number;
        int sum = 0;
    
        // do-while loop to sum numbers until zero is entered
        do {
            cout << "Enter a number (0 to stop): ";
            cin >> number;
            sum += number; // Add the entered number to sum
        } while (number != 0); // Continue until zero is entered
    
        cout << "Total sum: " << sum << endl;
    
        return 0;
    }
    

    Question 5(b). Explain the concept of class template. What is the advantage of using a template? (5 Marks)

    Answer:

    A class template in C++ allows you to define a generic class that can operate with any data type. This provides the ability to create a single class definition that can work with different data types without having to rewrite the class for each type. A class template is defined using the template keyword followed by template parameters in angle brackets .

    #include <iostream>
    using namespace std;
    
    template <typename T>
    class Calculator {
    public:
        T add(T a, T b) {
            return a + b;
        }
    
        T subtract(T a, T b) {
            return a - b;
        }
    };
    
    int main() {
        Calculator<int> intCalc;
        cout << "Int add: " << intCalc.add(5, 3) << endl;
        cout << "Int subtract: " << intCalc.subtract(5, 3) << endl;
    
        Calculator<double> doubleCalc;
        cout << "Double add: " << doubleCalc.add(5.5, 3.2) << endl;
        cout << "Double subtract: " << doubleCalc.subtract(5.5, 3.2) << endl;
    
        return 0;
    }
    

    Advantages of Using Templates:

    • Code Reusability: Templates allow the creation of generic classes and functions that can be reused with different data types, reducing code duplication.
    • Type Safety: Templates provide type-safe code, as the compiler ensures that the operations on the template parameters are valid for the specified types.
    • Flexibility: Templates offer flexibility in programming by enabling the definition of algorithms and data structures that work with any data type, including user-defined types.
    • Performance: Templates are resolved at compile time, which can lead to more efficient code compared to runtime polymorphism using inheritance and virtual functions.
    • Maintainability: Templates help in maintaining code by centralizing the logic for multiple data types in a single definition, making it easier to update and manage.

    Question 5(c). What is a virtual function? Explain the need for a virtual function. (5 Marks)

    Answer:

    A virtual function in C++ is a member function of a class that can be overridden in its derived classes. It is declared using the virtual keyword in the base class and is intended to be redefined in any derived class. Virtual functions ensure that the correct function is called for an object, regardless of the type of reference (or pointer) used for the function call.

    Need for Virtual Function:

    • Polymorphism: Virtual functions enable polymorphism, allowing a base class reference or pointer to call methods of derived class objects. This supports dynamic method dispatch, where the method to be invoked is determined at runtime based on the actual object type.
    • Dynamic Binding: Without virtual functions, C++ uses static binding, where the function call is resolved at compile time based on the type of the pointer or reference. Virtual functions facilitate dynamic binding (or late binding), where the function call is resolved at runtime.
    • Code Flexibility and Extensibility: Virtual functions allow you to design flexible and extensible systems. New derived classes can be added with overridden behavior without changing the existing code that uses pointers or references to the base class.
    • Interface Implementation: Virtual functions define interfaces in base classes that derived classes must implement. This promotes consistency and enforces a contract that derived classes must adhere to.

    Question 5(d). What design concepts would you keep in mind while designing an object-oriented application? Explain with an example. (5 Marks)

    Answer:

    When designing an object-oriented application, several key design concepts ensure that the application is robust, maintainable, and extensible. These concepts include encapsulation, inheritance, polymorphism, abstraction, and design principles such as SOLID principles.

    • Encapsulation: Encapsulation involves bundling the data (attributes) and methods (functions) that operate on the data into a single unit, or class. It also involves restricting direct access to some of the object's components, which is typically achieved using access specifiers (private, protected, public).
    • Inheritance: Inheritance allows a new class (derived class) to inherit attributes and methods from an existing class (base class). This promotes code reuse and establishes a natural hierarchical relationship between classes.
    • Polymorphism: Polymorphism allows objects of different classes to be treated as objects of a common base class. It enables one interface to be used for a general class of actions, typically through the use of virtual functions.
    • Abstraction: Abstraction involves hiding the complex implementation details of a system and exposing only the necessary and relevant aspects of the system. It simplifies the interface and makes the software easier to interact with.
    • SOLID Principles: These are five design principles intended to make software designs more understandable, flexible, and maintainable:
      Single Responsibility Principle (SRP): A class should have only one reason to change, meaning it should have only one job or responsibility.
      Open/Closed Principle (OCP): Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.
      Liskov Substitution Principle (LSP): Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.
      Interface Segregation Principle (ISP): No client should be forced to depend on methods it does not use.
      Dependency Inversion Principle (DIP): High-level modules should not depend on low-level modules. Both should depend on abstractions.