2080

CSC166 · TU past paper

Object Oriented Programming 2080 question paper

The complete TU 2080 exam paper for Object Oriented Programming (CSC166), all 12 questions with solved model answers written to the mark scheme.

Tap a question to open its answer.

  1. 110 marksTypes of inheritanceAnswer

    State the use of new operator. An educational institute wishes to maintain a data of its employee. The hierarchical relationships of related classes are as follows. Define all the classes to represent above hierarchy and define functions to retrieve individual information as and when required.[10]

    New Operator and Employee Hierarchy in C++

    Use of new Operator (2 marks)

    The new operator in C++ is used for dynamic memory allocation. It allocates memory at runtime from the heap (free store) and returns a pointer to the beginning of the allocated memory block.

    Key uses:

    • Allocates memory for a variable or object during program execution
    • Returns a pointer to the allocated memory
    • Memory persists until explicitly released using delete
    • Syntax: pointer_variable = new data_type;

    Example:

    int *ptr = new int;        // allocates memory for one integer
    int *arr = new int[10];    // allocates memory for array of 10 integers
    delete ptr;                // free single variable
    delete[] arr;              // free array
    

    Employee Hierarchy Program (8 marks)

    Class Hierarchy Structure

               Employee
              (name, emp_id)
                   |
            ________________
            |               |
        Teacher          Officer
      (subject,        (department,
       salary)           salary)
            |
       Typist
      (speed,
       salary)
    

    Complete C++ Program

    #include<iostream>
    #include<string>
    using namespace std;
    
    // ─── Base Class ───────────────────────────────────────────────
    class Employee {
    protected:
        string name;
        int emp_id;
    
    public:
        // Function to set employee data
        void getEmployeeData() {
            cout << "Enter Employee Name: ";
            cin >> name;
            cout << "Enter Employee ID: ";
            cin >> emp_id;
        }
    
        // Function to display employee data
        void showEmployeeData() {
            cout << "\n--- Employee Information ---" << endl;
            cout << "Name     : " << name << endl;
            cout << "Emp ID   : " << emp_id << endl;
        }
    };
    
    // ─── Derived Class: Teacher ────────────────────────────────────
    class Teacher : public Employee {
    protected:
        string subject;
        float salary;
    
    public:
        // Function to set teacher data
        void getTeacherData() {
            getEmployeeData();   // call base class function
            cout << "Enter Subject Specialization: ";
            cin >> subject;
            cout << "Enter Salary: ";
            cin >> salary;
        }
    
        // Function to display teacher data
        void showTeacherData() {
            showEmployeeData();  // call base class function
            cout << "Subject  : " << subject << endl;
            cout << "Salary   : " << salary << endl;
        }
    };
    
    // ─── Derived Class: Typist (derived from Teacher) ─────────────
    class Typist : public Teacher {
    private:
        int typingSpeed;   // words per minute
    
    public:
        // Function to set typist data
        void getTypistData() {
            getTeacherData();
            cout << "Enter Typing Speed (wpm): ";
            cin >> typingSpeed;
        }
    
        // Function to display typist data
        void showTypistData() {
            showTeacherData();
            cout << "Typing Speed: " << typingSpeed << " wpm" << endl;
        }
    };
    
    // ─── Derived Class: Officer ────────────────────────────────────
    class Officer : public Employee {
    private:
        string department;
        float salary;
    
    public:
        // Function to set officer data
        void getOfficerData() {
            getEmployeeData();   // call base class function
            cout << "Enter Department: ";
            cin >> department;
            cout << "Enter Salary: ";
            cin >> salary;
        }
    
        // Function to display officer data
        void showOfficerData() {
            showEmployeeData();  // call base class function
            cout << "Department: " << department << endl;
            cout << "Salary    : " << salary << endl;
        }
    };
    
    // ─── Main Function ─────────────────────────────────────────────
    int main() {
        int choice;
        cout << "===== Educational Institute Employee System =====" << endl;
        cout << "1. Teacher\n2. Typist\n3. Officer" << endl;
        cout << "Enter your choice: ";
        cin >> choice;
    
        // Using new operator for dynamic object creation
        if (choice == 1) {
            Teacher *t = new Teacher;
            t->getTeacherData();
            t->showTeacherData();
            delete t;
        }
        else if (choice == 2) {
            Typist *ty = new Typist;
            ty->getTypistData();
            ty->showTypistData();
            delete ty;
        }
        else if (choice == 3) {
            Officer *o = new Officer;
            o->getOfficerData();
            o->showOfficerData();
            delete o;
        }
        else {
            cout << "Invalid choice!" << endl;
        }
    
        return 0;
    }
    

    Sample Output

    ===== Educational Institute Employee System =====
    1. Teacher
    2. Typist
    3. Officer
    Enter your choice: 1
    
    Enter Employee Name: Ram
    Enter Employee ID: 101
    Enter Subject Specialization: Mathematics
    Enter Salary: 45000
    
    --- Employee Information ---
    Name     : Ram
    Emp ID   : 101
    Subject  : Mathematics
    Salary   : 45000
    

    Key Concepts Used

    ConceptApplication
    Base ClassEmployee holds common data (name, emp_id)
    InheritanceTeacher, Officer inherit from Employee; Typist inherits from Teacher
    protected membersAllow derived classes to access base class data directly
    new operatorUsed to create objects dynamically at runtime
    Function overridingEach derived class defines its own show and get functions
    public inheritanceDerived class inherits all public and protected members of base class

    **

  2. 210 marksInitialization of class objectsAnswer

    What is constructor? Why constructor is needed in a class? Illustrate the types of constructor with an example.[10]

    Constructor in C++

    Definition of Constructor

    A constructor is a special member function of a class that is automatically called when an object of that class is created. It has the same name as the class and is used to initialize the data members of the class.

    Key Characteristics of a Constructor:

    • It has the same name as the class
    • It has no return type (not even void)
    • It is called automatically when an object is created
    • It cannot be inherited, but a derived class can call the base class constructor
    • It can be overloaded

    Why is a Constructor Needed in a Class?

    When an object is created, its data members may contain garbage values if not initialized. A constructor is needed because:

    1. Automatic Initialization: It automatically initializes data members when an object is created, avoiding garbage values.
    2. Memory Allocation: It can allocate dynamic memory using the new operator at the time of object creation.
    3. No Manual Call Required: Unlike regular functions, the programmer does not need to explicitly call it; it runs automatically.
    4. Ensures Valid State: It ensures that an object always starts in a valid, well-defined state.
    5. Resource Acquisition: It can open files, establish connections, or acquire any resources needed by the object.

    As seen in the notes, a class like string uses a constructor string(char *c) to allocate memory using new and copy data, ensuring the object is properly set up from the moment it is created.


    Types of Constructors

    There are three main types of constructors in C++:

    1. Default Constructor

    A constructor that takes no arguments (or has all default arguments). It is called automatically when an object is created without any parameters.

    #include <iostream>
    using namespace std;
    
    class Student {
    private:
        int rollNo;
        float marks;
    
    public:
        // Default Constructor
        Student() {
            rollNo = 0;
            marks = 0.0;
            cout << "Default Constructor called!" << endl;
        }
    
        void display() {
            cout << "Roll No: " << rollNo << ", Marks: " << marks << endl;
        }
    };
    
    int main() {
        Student s1;   // Default constructor is called automatically
        s1.display();
        return 0;
    }
    

    Output:

    Default Constructor called!
    Roll No: 0, Marks: 0.0
    

    2. Parameterized Constructor

    A constructor that accepts one or more arguments to initialize data members with specific values at the time of object creation.

    #include <iostream>
    using namespace std;
    
    class Student {
    private:
        int rollNo;
        float marks;
    
    public:
        // Parameterized Constructor
        Student(int r, float m) {
            rollNo = r;
            marks = m;
            cout << "Parameterized Constructor called!" << endl;
        }
    
        void display() {
            cout << "Roll No: " << rollNo << ", Marks: " << marks << endl;
        }
    };
    
    int main() {
        Student s1(101, 85.5);   // Parameterized constructor called
        Student s2(102, 90.0);
        s1.display();
        s2.display();
        return 0;
    }
    

    Output:

    Parameterized Constructor called!
    Parameterized Constructor called!
    Roll No: 101, Marks: 85.5
    Roll No: 102, Marks: 90.0
    

    3. Copy Constructor

    A constructor that creates a new object as a copy of an existing object. It takes a reference to an object of the same class as its argument.

    #include <iostream>
    using namespace std;
    
    class Student {
    private:
        int rollNo;
        float marks;
    
    public:
        // Parameterized Constructor
        Student(int r, float m) {
            rollNo = r;
            marks = m;
        }
    
        // Copy Constructor
        Student(const Student &s) {
            rollNo = s.rollNo;
            marks = s.marks;
            cout << "Copy Constructor called!" << endl;
        }
    
        void display() {
            cout << "Roll No: " << rollNo << ", Marks: " << marks << endl;
        }
    };
    
    int main() {
        Student s1(101, 85.5);      // Parameterized constructor
        Student s2(s1);             // Copy constructor called
        s1.display();
        s2.display();
        return 0;
    }
    

    Output:

    Copy Constructor called!
    Roll No: 101, Marks: 85.5
    Roll No: 101, Marks: 85.5
    

    Summary Table

    TypeArgumentsPurpose
    Default ConstructorNoneInitializes members with default values
    Parameterized ConstructorOne or moreInitializes members with given values
    Copy ConstructorReference to same class objectCreates a copy of an existing object

    Note on Constructor vs Destructor

    A class can have both a constructor and a destructor (~classname()). While the constructor initializes/creates the object, the destructor cleans up (e.g., frees memory allocated by new) when the object goes out of scope. Together they manage the full lifecycle of an object.

  3. 310 marksFunctionsAnswer

    When inline functions may not work? Define and write syntax for default arguments. Write a program to display N number of characters by using default arguments for both parameters. Assume that the function takes two arguments, one character to be printed and other how many times the character to be printed respectively.[10]

    Answer: Inline Functions, Default Arguments, and Program


    Part 1: When Inline Functions May NOT Work (3 marks)

    The compiler may ignore the inline request and treat the function as a normal function in the following situations:

    1. Function contains loops - If the inline function contains looping statements such as for, while, or do-while, the compiler will not inline it.

    2. Function contains static variables - If the function has static local variables declared inside it, it cannot be inlined.

    3. Function is recursive - A recursive inline function (a function that calls itself) cannot be expanded inline.

    4. Function contains switch or goto statements - Functions containing switch or goto control statements may not be inlined.

    5. Function is too large/complex - If the function body is too large, inlining it would increase code size significantly, so the compiler refuses to inline it.

    6. Function returns a value and contains multiple return statements - In some cases, multiple return points prevent inlining.

    Note: The inline keyword is only a request to the compiler, not a command. The compiler has the final decision.


    Part 2: Default Arguments - Definition and Syntax (3 marks)

    Definition:

    A default argument is a value provided in a function declaration that is automatically assigned by the compiler if the calling function does not provide a value for that argument. It allows a function to be called with fewer arguments than it is defined to accept.

    Key Rules:

    • Default arguments must be specified from right to left (trailing arguments first).
    • Once a default value is assigned to a parameter, all parameters to its right must also have default values.
    • Default values are specified in the function declaration (prototype).

    Syntax:

    return_type function_name(datatype param1, datatype param2 = default_value);
    

    Example of syntax with two default arguments:

    void display(char ch = '*', int n = 10);
    

    Here, if no arguments are passed, ch defaults to '*' and n defaults to 10.


    Part 3: Program to Display N Number of Characters Using Default Arguments (4 marks)

    #include <iostream>
    using namespace std;
    
    // Function declaration with default arguments
    // ch = character to print (default: '*')
    // n  = number of times to print (default: 3)
    void printChar(char ch = '*', int n = 3);
    
    int main()
    {
        cout << "Using both default arguments (no arguments passed):" << endl;
        printChar();
        // Output: ***
    
        cout << "\nUsing one argument (only character passed):" << endl;
        printChar('#');
        // Output: ###
    
        cout << "\nUsing both arguments (no defaults used):" << endl;
        printChar('@', 7);
        // Output: @@@@@@@
    
        cout << "\nUsing character '$' with 5 times:" << endl;
        printChar('$', 5);
        // Output: $$$$$
    
        return 0;
    }
    
    // Function definition
    void printChar(char ch, int n)
    {
        cout << "Character: " << ch << " | Printed " << n << " time(s): ";
        for (int i = 1; i <= n; i++)
        {
            cout << ch;
        }
        cout << endl;
    }
    

    Output:

    Using both default arguments (no arguments passed):
    Character: * | Printed 3 time(s): ***
    
    Using one argument (only character passed):
    Character: # | Printed 3 time(s): ###
    
    Using both arguments (no defaults used):
    Character: @ | Printed 7 time(s): @@@@@@@
    
    Using character '$' with 5 times:
    Character: $ | Printed 5 time(s): $$$$$
    

    Explanation of the Program:

    Function Callch usedn usedSource
    printChar()'*'3Both defaults used
    printChar('#')'#'3n default used
    printChar('@', 7)'@'7No defaults used
    printChar('$', 5)'$'5No defaults used

    Important Note: Default arguments are written in the function declaration/prototype, not in the definition. The compiler substitutes the default value when the argument is omitted in the function call.

  4. 45 marksStream Class Hierarchy for Console Input/OAnswer

    What do you mean by stream? Explain different stream class for file input/output. [5]

    Stream and Stream Classes for File I/O in C++

    What is a Stream?

    A stream is a sequence of bytes flowing between a program and an input/output device (such as a file, keyboard, or screen). It acts as an abstraction layer between the program and the actual device.

    • We have been using the iostream standard library, which provides cin and cout for reading from standard input and writing to standard output respectively.
    • For file-based I/O, C++ provides another standard library called fstream, which defines specialized stream classes to read from and write to files.

    To perform file processing in C++, the header file <fstream> must be included in the source file.


    Stream Classes for File Input/Output

    The fstream library defines three main data types (stream classes):


    1. ifstream (Input File Stream)

    • Represents the input file stream.
    • Used to read information from files.
    • Derived from istream.

    Example:

    #include <fstream>
    using namespace std;
    
    int main() {
        ifstream fin;
        fin.open("data.txt");   // open file for reading
        char ch;
        while (fin >> ch)
            cout << ch;
        fin.close();
        return 0;
    }
    

    2. ofstream (Output File Stream)

    • Represents the output file stream.
    • Used to create files and write information to files.
    • Derived from ostream.

    Example:

    #include <fstream>
    using namespace std;
    
    int main() {
        ofstream fout;
        fout.open("data.txt");   // open file for writing
        fout << "Hello, File!";
        fout.close();
        return 0;
    }
    

    3. fstream (File Stream)

    • Represents the general file stream.
    • Has the capabilities of both ifstream and ofstream.
    • Can create files, write information to files, and read information from files.

    Example:

    #include <fstream>
    using namespace std;
    
    int main() {
        fstream file;
        file.open("data.txt", ios::in | ios::out);  // read and write
        file << "Writing data";
        file.close();
        return 0;
    }
    

    Summary Table

    ClassPurposeOperation
    ifstreamInput file streamRead from files
    ofstreamOutput file streamCreate and write to files
    fstreamGeneral file streamRead and write to files

    File Pointer Manipulation Functions

    The stream classes also support functions to manage file pointer positions:

    FunctionDescription
    seekg()Moves the get (input) pointer to a specified location
    seekp()Moves the put (output) pointer to a specified location
    tellg()Returns the current position of the get pointer
    tellp()Returns the current position of the put pointer

    These functions allow random access within a file, enabling reading or writing at any desired position.

  5. 55 marksStatic membersAnswer

    Write a C++ program to display the number of objects created using static member. [5]

    A static data member is shared by all objects of a class. It is initialized only once and retains its value throughout the program. It is used here to count how many objects have been created, because every time a constructor is called (...

  6. 65 marksFriend function and Static functionAnswer

    Explain about friend function and friend class with an example. [5]

    A friend function is a function that is not a member of a class but has access to the private and protected members of that class. - The declaration is placed inside the class body (in either private or public section) preceded by the ke...

  7. 75 marksConstructor and Destructor in derived clasAnswer

    Describe the chain of constructors and destructors in inheritance. [5]

    In inheritance, when an object of a derived class is created or destroyed, constructors and destructors of both the base class and the derived class are automatically invoked in a specific order. --- When a derived class object is create...

  8. 85 marksConcept of Virtual functionsAnswer

    What is the use of reinterpret cast operator? Why do we need virtual function? [5]

    Reinterpret Cast Operator and Virtual Functions


    Part 1: reinterpret_cast Operator

    Definition

    reinterpret_cast is a cast operator in C++ that converts one pointer type to another pointer type, even if the types are unrelated. It forces the compiler to treat the bit pattern of one type as if it were another type entirely.

    As noted in the reference: "A cast operator is a special operator that forces one data type to be converted into another."

    Syntax

    reinterpret_cast<new_type>(expression);
    

    Uses of reinterpret_cast

    Use CaseDescription
    Pointer to pointer conversionConvert int* to char* or any unrelated pointer type
    Pointer to integer conversionStore a pointer value as an integer
    Integer to pointer conversionConvert an integer back to a pointer
    Low-level memory manipulationUsed in hardware/system programming

    Example

    #include <iostream>
    using namespace std;
    
    int main() {
        int a = 65;
        int* ptr = &a;
    
        // Reinterpret int pointer as char pointer
        char* cptr = reinterpret_cast<char*>(ptr);
    
        cout << "Integer value: " << *ptr << endl;
        cout << "Char interpretation: " << *cptr << endl;  // Output: A
    
        return 0;
    }
    

    Key Points

    • It does not check type safety; the programmer is responsible for correctness.
    • It is the most dangerous cast and should be used with caution.
    • Commonly used in low-level programming, such as device drivers or memory-mapped I/O.

    Part 2: Why Do We Need Virtual Functions?

    Problem Without Virtual Functions

    When a base class pointer points to a derived class object, calling an overridden function without virtual will always call the base class version due to static (early) binding.

    From the reference: "When a member function is not virtual, the function called is determined only by the type of the expression to the left of dot (.) or arrow (->) operator. This is called the static type."

    What is a Virtual Function?

    A virtual function is a member function declared in the base class using the keyword virtual, which tells the compiler to perform late binding (runtime binding) so that the correct overridden function is called based on the actual object type, not the pointer type.

    From the reference: "Late Binding is achieved using Virtual functions."

    Example Demonstrating the Need

    #include <iostream>
    using namespace std;
    
    class Base {
    public:
        virtual void print() {          // virtual function
            cout << "Print Base class" << endl;
        }
        void show() {                   // non-virtual function
            cout << "Show Base class" << endl;
        }
    };
    
    class Derived : public Base {
    public:
        void print() {
            cout << "Print Derived class" << endl;
        }
        void show() {
            cout << "Show Derived class" << endl;
        }
    };
    
    int main() {
        Base* bptr;
        Derived d;
        bptr = &d;
    
        bptr->print();  // Virtual  -> calls Derived version (runtime binding)
        bptr->show();   // Non-virtual -> calls Base version (compile-time binding)
    
        return 0;
    }
    

    Output

    Print Derived class
    Show Base class
    

    Why We Need Virtual Functions

    1. Runtime Polymorphism: Enables the correct function to be called based on the actual object at runtime, not the pointer type.
    2. Flexibility: A single base class pointer can manage objects of multiple derived classes and call appropriate methods.
    3. Extensibility: New derived classes can be added without changing existing code.
    4. Correct Overriding Behavior: Without virtual, overriding does not work as expected through base class pointers.

    Important Note

    From the reference: "If we have created a virtual function in the base class and it is being overridden in the derived class, then we don't need the virtual keyword in the derived class -- functions are automatically considered as virtual functions in the derived class."


    Summary Table

    Featurereinterpret_castVirtual Function
    PurposeLow-level type conversionRuntime polymorphism
    BindingN/ALate (runtime) binding
    SafetyUnsafe, programmer's responsibilitySafe, resolved by vtable
  9. 95 marksAbstract class and pure virtual functionsAnswer

    Differentiate between concrete class and abstract class. Define class template and function template with syntax. [5]

    --- As stated in the notes: "There are two main types of classes: Abstract class and Concrete class. The main difference between the two arises from the level of implementation of their method functionalities." Feature Concrete Class Abs...

  10. 105 marksExceptional HandlingAnswer

    List any two operators that cannot be overloaded. Write a program to handle the possible exception in taking 'age' as input. Here the exception must be thrown in the input is negative or greater than 200. [5]

    Answer

    Two Operators That Cannot Be Overloaded

    The following operators cannot be overloaded in C++:

    OperatorName
    ::Scope Resolution Operator
    ?:Ternary (Conditional) Operator

    Other examples include: . (member access), .* (pointer to member), sizeof.


    Program to Handle Exception for Invalid Age Input

    The program throws an exception if the entered age is negative or greater than 200.

    #include <iostream>
    using namespace std;
    
    // Custom Exception Class for Age
    class AgeException {
    private:
        string message;
    public:
        AgeException(string msg) {
            message = msg;
        }
        string getMessage() {
            return message;
        }
    };
    
    // Function to validate and return age
    int getAge(int age) {
        if (age < 0) {
            throw AgeException("Invalid age: Age cannot be negative.");
        }
        else if (age > 200) {
            throw AgeException("Invalid age: Age cannot be greater than 200.");
        }
        return age;
    }
    
    int main() {
        int age;
        cout << "Enter age: ";
        cin >> age;
    
        try {
            int validAge = getAge(age);
            cout << "Valid Age entered: " << validAge << endl;
        }
        catch (AgeException e) {
            cout << "Exception caught: " << e.getMessage() << endl;
        }
    
        return 0;
    }
    

    Sample Output

    Case 1: Valid Input

    Enter age: 25
    Valid Age entered: 25
    

    Case 2: Negative Age

    Enter age: -5
    Exception caught: Invalid age: Age cannot be negative.
    

    Case 3: Age Greater Than 200

    Enter age: 250
    Exception caught: Invalid age: Age cannot be greater than 200.
    

    Explanation

    • The AgeException class is a user-defined exception class that stores an error message.
    • The getAge() function throws an AgeException object when the age is invalid (negative or > 200).
    • In main(), the try block calls getAge() and the catch block handles the thrown exception by printing the error message.
    • This approach separates error-handling code from normal program code, which is one of the key uses of exception handling as stated in the notes.
  11. 115 marksOperator functions as a class membersAnswer

    Create a class named Point with data members x(int) and y(int). Add operator overloading to find the Euclidean distance between two points. [5]

    Operator overloading allows us to redefine the behavior of an operator for user-defined types (classes). Here, we overload the - operator (or a suitable operator) to compute the Euclidean distance between two Point objects. The Euclidean...

  12. 125 marksConcept of Virtual functionsAnswer

    Explain the reason for member function overriding when using virtual function. [5]

    Member Function Overriding with Virtual Functions

    Definition and Concept

    Overriding means defining a member function with the same name in both the base class and the derived class. When such a function exists in both classes, the derived class function overrides the base class function.

    Virtual function is a member function in the base class that is declared using the keyword virtual. When virtual functions are used, different functions can be executed by the same function call statement.


    Reason for Member Function Overriding When Using Virtual Functions

    The Core Problem (Without Virtual)

    When a base class pointer is used to point to a derived class object, without virtual, the function call is resolved at compile time (static binding). This means the base class version of the function always gets called, even when the pointer is pointing to a derived class object. This defeats the purpose of inheritance and polymorphism.

    How Virtual Solves It

    When a function is declared virtual in the base class, the function call is resolved at runtime (dynamic binding). The function called is determined by the actual type of the object pointed to, not the type of the pointer.

    As per the notes: "When a member function is virtual, the function called is determined by the actual most derived type of the object named by the expression left of the dot or pointed to by the expression left of the arrow. This is called the dynamic type."


    Types of Polymorphism Involved

    TypeHow AchievedBinding
    Compile-time (Static)OverloadingAt compile time
    Runtime (Dynamic)Overriding with virtualAt runtime

    Example

    #include <iostream>
    using namespace std;
    
    class base {
    public:
        virtual void print() {          // virtual function
            cout << "Print base class" << endl;
        }
        void show() {                   // non-virtual function
            cout << "Show base class" << endl;
        }
    };
    
    class derived : public base {
    public:
        void print() {                  // overrides base class print()
            cout << "Print derived class" << endl;
        }
        void show() {
            cout << "Show derived class" << endl;
        }
    };
    
    int main() {
        base *bptr;
        derived d;
        bptr = &d;          // base pointer pointing to derived object
    
        bptr->print();      // virtual function -> runtime binding
        bptr->show();       // non-virtual function -> compile-time binding
        return 0;
    }
    

    Output

    Print derived class
    Show base class
    

    Explanation of Output

    • bptr->print() calls the derived class version because print() is declared virtual in the base class. The function is bound at runtime based on the actual object type (derived).
    • bptr->show() calls the base class version because show() is not virtual. It is bound at compile time based on the pointer type (base).

    Key Points

    1. Runtime polymorphism is achieved only through a pointer or reference of a base class type.
    2. A base class pointer can point to objects of both base and derived classes.
    3. If a virtual function in the base class is overridden in the derived class, the virtual keyword is not required again in the derived class; the function is automatically considered virtual.
    4. Without virtual, member function overriding cannot achieve dynamic dispatch, making the concept of runtime polymorphism impossible.