📚 CSE 2100: Object-Oriented Programming Laboratory

Exhaustive Academic Practice Problem Sheet & Lab Exam Drill

This document serves as the comprehensive, exam-oriented practice workbook for CSE 2100: Object-Oriented Programming Laboratory. It is meticulously structured to align with the KUET CSE syllabus, matching the exact difficulty, terminology, and formatting found in KUET class quizzes, live laboratory vivas, and the ECE 2k23 final lab tests.


🏷️ Topic 1 (Week 1 - Classes & Stream I/O)

Tier 1: Celsius Temperature Logger (Warm-Up)

  • Problem Statement: Create a simple class named Temperature that models a single day’s reading in Celsius. The program should accept a float value from standard input, store it securely, and display it with a simple message.
  • Class Blueprint & Constraints:
    • Private Data Members: float celsius
    • Public Member Functions:
      • void setTemp(float t): Assigns the input value to the private variable.
      • void display(): Prints the temperature to the console.
    • Constraints: No direct access to celsius is allowed in main(). Use stream-based I/O (cin and cout) exclusively. Do not use C-style formatting.
  • Sample Test Input & Expected Console Output:
    • Input: 32.5
    • Output: Recorded Temperature: 32.5°C
  • Common Student Pitfall / Bug to Watch For:
    • Missing Class Semicolon: Forgetting the semicolon ; after the closing brace of the class definition (class Temperature { ... };). This triggers a cryptic compiler error indicating that a type or definition is incomplete before main().
    • Stream Operator Reversal: Writing cin << temp; or cout >> temp;. Remember that the extraction operator >> points away from the stream object, whereas the insertion operator << points towards the stream.

Tier 2: KUET Student Registration Portal (Lab Test Standard)

  • Problem Statement: Design a program that models a university registration record. The class must encapsulate basic information about a student, enforce age validation, and use standard stream mechanisms to manage the state.
  • Class Blueprint & Constraints:
    • Private Data Members:
      • int rollNo
      • string name
      • int age
    • Public Member Functions:
      • void registerStudent(int r, string n, int a): Validates and records details. If the age is less than 18, set a default age of 18 and print a warning message.
      • void displayRecord() const: Prints the registered details in a clean format.
  • Sample Test Input & Expected Console Output:
    • Input:
      2103045
      Tahmid Hasan
      17
    • Output:
      Warning: Age 17 is below minimum. Defaulting to 18.
      Roll: 2103045 | Name: Tahmid Hasan | Age: 18
  • Common Student Pitfall / Bug to Watch For:
    • The getline Buffer Trap: When reading an integer with cin >> rollNo followed by a string using getline(cin, name), the trailing newline character \n remains in the stream buffer. The subsequent getline immediately consumes this newline as an empty string. You must use cin.ignore() to flush the newline before calling getline.

Tier 3: Namespace Collision and Specifier Diagnostics (Exam Trap)

  • Problem Statement: Read and analyze the following code segment. Identify all compilation errors, explain the precise rules violated, and show the corrected code.
    #include <iostream>
    struct Person {
    private:
        int age;
    public:
        void setAge(int a) { age = a; }
    };
     
    class Student {
        int roll;
        void setRoll(int r) { roll = r; }
    };
     
    int main() {
        Person p;
        p.age = 20;
        Student s;
        s.setRoll(101);
        return 0;
    }
  • Diagnostic Explanation & Corrected Code:
    1. Struct default visibility violation: In C++, a struct defaults to public access, but private blocks can still be declared. However, in main(), p.age = 20 fails because age is declared under a private label.
    2. Class default visibility violation: Members of a class are private by default. Thus, void setRoll(int r) in class Student is private. Attempting to call s.setRoll(101) in main() causes a compilation error.
    • Corrected Code:
      #include <iostream>
      using namespace std;
       
      struct Person {
      public: // Changed to public or removed private specifier
          int age;
      };
       
      class Student {
      private:
          int roll;
      public: // Added public specifier for interface methods
          void setRoll(int r) { roll = r; }
      };
       
      int main() {
          Person p;
          p.age = 20; // Legal now
          Student s;
          s.setRoll(101); // Legal now
          return 0;
      }

🏷️ Topic 2 (Week 3 - Member Functions & Arrays of Objects)

Tier 1: Geometry Inline Solver (Warm-Up)

  • Problem Statement: Implement a Rectangle class where the area-solving logic is defined outside the class using the scope resolution operator but optimized using compiler inlining.
  • Class Blueprint & Constraints:
    • Private Data Members: float length, float width
    • Public Member Functions:
      • void setDimensions(float l, float w): Declared inside, defined outside.
      • float getArea(): Declared inside, defined outside with the inline keyword.
  • Sample Test Input & Expected Console Output:
    • Input: 5.0 4.0
    • Output: Area: 20
  • Common Student Pitfall / Bug to Watch For:
    • Scope Operator Omission: Writing void setDimensions(float l, float w) { ... } outside the class without qualifying it as void Rectangle::setDimensions(...). The compiler treats this as a global function rather than a member function, causing a compilation error due to undefined variables (length and width).

Tier 2: Library Catalog Roster (Lab Test Standard)

  • Problem Statement: Create a program that manages a catalog of 3 books. The program must store titles, authors, and availability states. It should accept an array of objects, take dynamic user inputs via a loop, and pass objects as arguments to perform checkouts.
  • Class Blueprint & Constraints:
    • Private Data Members:
      • int bookID
      • char availability // ‘A’ = Available, ‘I’ = Issued
    • Public Data Members:
      • string title
      • string author
    • Public Member Functions:
      • void assignValues(int id, string t, string a, char status)
      • void issueBook(): Changes status to 'I'. Displays an error if already issued.
      • void displayDetails() const
    • Constraints: Model strictly around an array of objects. Use pass-by-const-reference to display information efficiently.
  • Sample Test Input & Expected Console Output:
    • Input:
      101 "The C++ Programming Language" "Bjarne" 'A'
      102 "OOP in C++" "Balagurusamy" 'I'
    • Output:
      --- Catalog ---
      ID: 101 | Title: The C++ Programming Language | Status: Available
      ID: 102 | Title: OOP in C++ | Status: Issued
  • Common Student Pitfall / Bug to Watch For:
    • Pass-by-Value Performance Cost: Passing large objects (like those containing strings) by value. This invokes the copy constructor unnecessarily, duplicating data on the stack. Correct this by passing by const reference: void showBook(const Book& b).

Tier 3: Private Nesting Helper Ambiguity (Exam Trap)

  • Problem Statement: Predict the output of the following code and explain how member function nesting behaves inside memory. Correct the compiler error.
    #include <iostream>
    using namespace std;
     
    class NumberSet {
    private:
        int num1, num2;
        int findMax() { return (num1 > num2) ? num1 : num2; }
    public:
        void setValues(int a, int b) { num1 = a; num2 = b; }
        void displayMax() {
            int maxVal = findMax(); // Nesting
            cout << "Max: " << maxVal << endl;
        }
    };
     
    int main() {
        NumberSet ns;
        ns.setValues(45, 82);
        ns.displayMax();
        cout << "Direct Max: " << ns.findMax() << endl; 
        return 0;
    }
  • Diagnostic Explanation & Corrected Code:
    • Error: The statement ns.findMax() inside main() fails to compile. findMax() is a private member function. Private helper functions can be nested (called directly inside public members like displayMax() without an object identifier), but they are strictly hidden from external scopes like main().
    • Corrected Code: Remove the unauthorized external call ns.findMax() in main().

🏷️ Topic 3 (Week 5 - Object Lifecycle & The Rule of Three/Four)

Tier 1: Value Reset Constructor (Warm-Up)

  • Problem Statement: Design an Item class that demonstrates automatic zero-initialization on creation using a default constructor, and custom initialization via a parameterized constructor.
  • Class Blueprint & Constraints:
    • Private Data Members: int itemCode, double price
    • Public Member Functions:
      • Item(): Default constructor initializing code to 0 and price to 0.0 using a constructor initializer list.
      • Item(int c, double p): Parameterized constructor.
      • void display() const
  • Sample Test Input & Expected Console Output:
    • Input: (Instance creation)
    • Output:
      Item 1: Code 0, Price 0
      Item 2: Code 105, Price 45.99
  • Common Student Pitfall / Bug to Watch For:
    • Most Vexing Parse: Declaring a default object as Item item1();. C++ interprets this statement as a prototype for a global function named item1 that takes no arguments and returns an Item object. Declare it as Item item1; without parentheses.

Tier 2: Dynamic Integer Sequence Manager (Lab Test Standard)

  • Problem Statement: Create a class named DynamicArray that wraps a raw heap-allocated integer array. To prevent dynamic memory corruption, implement the full Rule of Three/Four: a destructor, a deep-copy constructor, and a deep-copy assignment operator.
  • Class Blueprint & Constraints:
    • Private Data Members:
      • int* data
      • int size
    • Public Member Functions:
      • DynamicArray(int s, int defaultVal): Allocates dynamic memory.
      • DynamicArray(const DynamicArray& source): Deep copy constructor.
      • DynamicArray& operator=(const DynamicArray& rhs): Deep copy assignment operator. Must handle self-assignment guards.
      • ~DynamicArray(): Frees allocated heap memory.
      • void set(int index, int val)
      • void display() const
  • Sample Test Input & Expected Console Output:
    • Input Trace (in main):
      DynamicArray a1(3, 10);
      DynamicArray a2 = a1; // Copy Constructor
      a2.set(0, 99);
      DynamicArray a3;
      a3 = a1; // Assignment operator
    • Output:
      a1: 10 10 10
      a2: 99 10 10
      a3: 10 10 10
  • Common Student Pitfall / Bug to Watch For:
    • The Shallow Copy Disaster: Failing to write a custom copy constructor or assignment operator. The default compiler-generated versions copy pointers bit-by-bit. If a2 shallow-copies a1, both point to the same heap address. When one goes out of scope and calls delete[] data, the other pointer is left dangling. When the second object is destroyed, a catastrophic double-free runtime crash occurs.

Tier 3: Pass-by-Value Infinite Copy Recursion (Exam Trap)

  • Problem Statement: Analyze the compilation error or runtime crash in this segment. Detail the memory mechanics that cause this behavior and show how to fix it.
    #include <iostream>
    using namespace std;
     
    class Holder {
        int val;
    public:
        Holder(int v) : val(v) {}
        // Copy Constructor:
        Holder(Holder source) { 
            val = source.val;
        }
    };
     
    int main() {
        Holder h1(10);
        Holder h2 = h1;
        return 0;
    }
  • Diagnostic Explanation & Corrected Code:
    • The Trap: The copy constructor is declared with its parameter passed by value: Holder(Holder source). When h2 = h1 is compiled, C++ attempts to copy h1 into the parameter source. To copy an object by value, C++ must invoke the copy constructor. This recursive call requires another copy, which calls the copy constructor again, leading to an infinite compile-time recursion error or stack overflow at runtime.
    • The Fix: The parameter must be passed by reference, and ideally marked const for safety: Holder(const Holder& source).

🏷️ Topic 4 (Week 7 - Inheritance & Visibility Modes)

Tier 1: Single Public Employee (Warm-Up)

  • Problem Statement: Declare a base class Employee with protected variables and derive a class Manager publicly.
  • Class Blueprint & Constraints:
    • Base Class (Employee):
      • protected: string empName; int empID;
      • public: void setBase(string n, int id)
    • Derived Class (Manager):
      • private: string department;
      • public: void setManager(string n, int id, string dept)
      • void display() const
  • Sample Test Input & Expected Console Output:
    • Input: Alice 101 "Operations"
    • Output: ID: 101 | Name: Alice | Department: Operations
  • Common Student Pitfall / Bug to Watch For:
    • Private Inheritance Access Error: Writing class Manager : Employee without specifying public. In C++, the default inheritance mode for classes is private. This converts all public base members to private in the derived class, preventing main() from accessing setBase() on a Manager object.

Tier 2: Multi-Level Teacher Ledger (Lab Test Standard)

  • Problem Statement: Design a multi-level university database. Implement a base class Staff, a publicly derived class Teacher, and a derived class Professor. Master parameter passing through constructors and overriding the display function.
  • Class Blueprint & Constraints:
    • Class Staff:
      • protected: int staffID; string name;
      • public: Staff(int id, string n): Parameterized constructor.
      • void display() const
    • Class Teacher (inherits publicly from Staff):
      • protected: string subject;
      • public: Teacher(int id, string n, string sub): Parameterized constructor.
    • Class Professor (inherits publicly from Teacher):
      • private: string researchArea;
      • public: Professor(int id, string n, string sub, string res): Parameterized constructor. Must explicitly delegate attributes to parent constructors.
      • void display() const: Overrides parent display. Uses scope resolution Teacher::display() to reuse code.
  • Sample Test Input & Expected Console Output:
    • Input: 1202 "Dr. Sadik" "Algorithms" "Machine Learning"
    • Output: ID: 1202 | Name: Dr. Sadik | Subject: Algorithms | Research: Machine Learning
  • Common Student Pitfall / Bug to Watch For:
    • Omit Parent Delegation: Forgetting to call the parent class’s parameterized constructor from the child class’s initializer list. If omitted, the compiler tries to find a default constructor (Staff()) in the parent class. If Staff only defines a parameterized constructor, compilation fails with: no matching function for call to 'Staff::Staff()'.

Tier 3: Construction Order Trace and Delegation Failure (Exam Trap)

  • Problem Statement: Predict the exact console output of this multi-level hierarchy when an object is created and destroyed. Explain the rules governing constructor and destructor execution orders.
    #include <iostream>
    using namespace std;
     
    class A {
    public:
        A() { cout << "A constructor" << endl; }
        ~A() { cout << "A destructor" << endl; }
    };
     
    class B : public A {
    public:
        B() { cout << "B constructor" << endl; }
        ~B() { cout << "B destructor" << endl; }
    };
     
    class C : public B {
    public:
        C() { cout << "C constructor" << endl; }
        ~C() { cout << "C destructor" << endl; }
    };
     
    int main() {
        {
            C obj;
        }
        return 0;
    }
  • Diagnostic Explanation & Execution Sequence:
    • Constructor Order: Base constructors are executed first, layer-by-layer, to ensure that parent components are initialized before derived classes build upon them. Sequence: A constructor → B constructor → C constructor.
    • Destructor Order: Destructors execute in the reverse order of construction to ensure that derived components are safely cleaned up before the base elements they depend on are released. Sequence: C destructor → B destructor → A destructor.
    • Console Output:
      A constructor
      B constructor
      C constructor
      C destructor
      B destructor
      A destructor

🏷️ Topic 5 (Week 7/8 - Advanced Inheritance, Diamond Problem & Friends)

Tier 1: Multiple Parent Ambiguity Resolver (Warm-Up)

  • Problem Statement: Design a class Derived that inherits from two base classes ParentA and ParentB simultaneously. Show how to resolve naming conflicts when both parents implement a function named print().
  • Class Blueprint & Constraints:
    • Class ParentA: public: void print() { cout << "A"; }
    • Class ParentB: public: void print() { cout << "B"; }
    • Class Derived (inherits publicly from both ParentA and ParentB):
      • public: void resolveAndPrint(): Calls both parents’ print functions.
  • Sample Test Input & Expected Console Output:
    • Input: derivedObj.resolveAndPrint();
    • Output: A and B
  • Common Student Pitfall / Bug to Watch For:
    • Unresolved Ambiguity: Directly writing print(); inside Derived. The compiler cannot decide which parent function to execute and triggers an ambiguity error. You must explicitly qualify the calls using the scope resolution operator: ParentA::print() and ParentB::print().

Tier 2: Same-Class Friend Account Comparator (Lab Test Standard)

  • Problem Statement: Sourced from Lab Test 01 - ECE 2k23. Create a class Account to represent depositor records. Implement a same-class friend comparator function that compares the balances of two objects and returns the richer one. Handle the examiner’s paradox (“both if same”).
  • Class Blueprint & Constraints:
    • Private Data Members:
      • int depositorID
      • float balance
    • Public Data Members:
      • long acc_no
      • char type // ‘S’ = Savings, ‘C’ = Current
      • string depositorName
    • Public Member Functions:
      • void assignInitialValues(int id, string name, long acc, char t, float bal)
      • void display() const
    • Friend Functions:
      • friend Account compareBalance(Account a1, Account a2): Compares balances. Returns the richer object.
  • Sample Test Input & Expected Console Output:
    • Input:
      Acc1: 101 "Alice" 112233 'S' 5000.50
      Acc2: 102 "Bob" 445566 'C' 12000.75
    • Output:
      Comparing Balances...
      Richer Depositor: Bob (Balance: $12000.75)
  • Common Student Pitfall / Bug to Watch For:
    • Scope Resolution Friend Trap: Defining the global friend function outside the class with the class scope resolution prefix (e.g., Account Account::compareBalance(...)). A friend function is NOT a member function. It must be defined globally without Account:: or the friend keyword in its implementation.
    • The “Both if Same” Return Paradox: If the exam asks you to return “both if same”, a function returning a single Account scalar object cannot satisfy this. The industry standard is to return the first object (a1) and log a note detailing the syntactical constraint.

Tier 3: Virtual Base Class Offsets & Static Segment Placement (Exam Trap)

  • Problem Statement: Design a diamond hierarchy resolving variable duplication. Implement a static instance tracker inside the shared grandparent, and explain how virtual base classes change the memory layout.
  • Class Blueprint & Constraints:
    • Grandparent Person:
      • protected: string name;
      • static int activeInstances;
    • Intermediate Student (inherits virtual publicly from Person): class Student : virtual public Person
    • Intermediate Sports (inherits virtual publicly from Person): class Sports : virtual public Person
    • Derived Result (inherits publicly from Student and Sports)
  • Memory layout diagrams & Mechanics:
    • Without Virtual Inheritance: Result contains two disjoint copies of Person (via Student and Sports). Result::name is ambiguous.
    • With Virtual Inheritance: The compiler strips the base class out of the normal layout, placing a single shared Person subobject at the end of the memory block. It inserts a hidden virtual table offset pointer (vptr offset) inside Student and Sports subobjects to find the shared data.
    • Static Member Constraints: Static variables (like activeInstances) do not live in the stack space of instantiated objects. They reside in the global static memory segment. Thus, static member functions do not possess a this pointer and cannot access name.
  • Diagnostic Correct Code:
    #include <iostream>
    #include <string>
    using namespace std;
     
    class Person {
    protected:
        string name;
    public:
        static int activeInstances; // Declared inside [v2_friend]
        Person() { activeInstances++; }
    };
     
    // Initialize static member in global scope
    int Person::activeInstances = 0; // Essential [v2_friend]
     
    class Student : virtual public Person {};
    class Sports : virtual public Person {};
    class Result : public Student, public Sports {
    public:
        void setName(string n) { name = n; } // Direct, unambiguous!
    };

🏷️ Topic 6 (Week 9 - The this Pointer & Operator Overloading)

Tier 1: Cascading Calculator with this (Warm-Up)

  • Problem Statement: Write a calculator class that allows method chaining (cascading) to compute mathematical results in a single line of code.
  • Class Blueprint & Constraints:
    • Private Data Members: int total
    • Public Member Functions:
      • Calculator(): Constructor.
      • Calculator& add(int val): Adds to total and returns the dereferenced this pointer (*this).
      • Calculator& sub(int val): Subtracts from total and returns *this.
      • void printResult() const
  • Sample Test Input & Expected Console Output:
    • Input Trace: calc.add(20).sub(5).printResult();
    • Output: Total: 15
  • Common Student Pitfall / Bug to Watch For:
    • Returning by Value in Cascading: Declaring the return type as Calculator instead of Calculator& (returning by reference). Returning by value creates a temporary copy of the object on the stack at each step, leaving the original object unchanged after the first chained call.

Tier 2: Streaming Complex Coordinate System (Lab Test Standard)

  • Problem Statement: Design a class Complex to model complex numbers. Overload the binary operator + to add two coordinates, and overload standard I/O stream operators (<< and >>) to read and write complex numbers naturally.
  • Class Blueprint & Constraints:
    • Private Data Members: float real, float imag
    • Public Member Functions:
      • Complex(float r = 0, float i = 0): Overloaded default constructor.
      • Complex operator+(const Complex& rhs) const: Binary member addition overload.
    • Friend Functions:
      • friend ostream& operator<<(ostream& out, const Complex& c): Custom streaming display.
      • friend istream& operator>>(istream& in, Complex& c): Custom streaming input.
  • Sample Test Input & Expected Console Output:
    • Input Trace:
      Enter Real: 4.5
      Enter Imaginary: 2.3
    • Output:
      Coordinates entered: 4.5 + i2.3
  • Common Student Pitfall / Bug to Watch For:
    • Streaming Member Operator Error: Attempting to overload << or >> as member functions. The left-hand side of a streaming expression (e.g., cout << c1) is an ostream object, not your custom class object. Since you cannot modify the standard std::ostream class, streaming overloads must be implemented as global friend functions.

Tier 3: Unary Prefix vs Postfix Dummy Parameter Semantics (Exam Trap)

  • Problem Statement: Implement prefix and postfix increment overloads for a custom coordinate pointer class Point. Explain the purpose of the dummy integer argument (int) in postfix overloads and analyze its memory overhead.
  • Class Blueprint & Constraints:
    • Private Data Members: int x, int y
    • Public Member Functions:
      • Point& operator++(): Prefix increment (++p). Returns by reference.
      • Point operator++(int): Postfix increment (p++). Takes a dummy int parameter. Returns by value.
  • Under-the-Hood Comparison:
    • Prefix (++p): Direct modification. Returns the updated object reference (*this), avoiding copying overhead.
    • Postfix (p++): Requires saving the original state in a temporary object (Point temp = *this), incrementing the active object, and returning the temp copy by value. This introduces a performance penalty because it requires allocating and copying a temporary object on the stack.
  • Correct Code Implementation:
    #include <iostream>
    using namespace std;
     
    class Point {
    private:
        int x, y;
    public:
        Point(int x = 0, int y = 0) : x(x), y(y) {}
     
        // Prefix
        Point& operator++() {
            ++x; ++y;
            return *this;
        }
     
        // Postfix: Note dummy 'int' argument
        Point operator++(int) {
            Point temp = *this; // Save state [v3_ops]
            ++x; ++y;           // Modify [v3_ops]
            return temp;        // Return original copy [v3_ops]
        }
     
        void show() { cout << "(" << x << "," << y << ")" << endl; }
    };

🏷️ Topic 7 (Week 9/10 - Polymorphism & Dynamic Binding)

Tier 1: Graphic Dynamic Binder (Warm-Up)

  • Problem Statement: Create a simple interface using virtual functions to demonstrate dynamic dispatch.
  • Class Blueprint & Constraints:
    • Base Class Shape: public: virtual void draw() { cout << "Base"; }
    • Derived Class Circle: public: void draw() override { cout << "Circle"; }
    • Constraints: Bind a base pointer Shape* ptr = new Circle() and invoke drawing logic dynamically.
  • Sample Test Input & Expected Console Output:
    • Trace: ptr->draw();
    • Output: Circle
  • Common Student Pitfall / Bug to Watch For:
    • Object Slicing: Assigning a derived object to a base object by value (e.g., Shape s = Circle();). This strips away the derived class components, copying only the base subobject. The virtual pointer (vptr) is reset to point to the base class’s vtable, completely destroying polymorphic behavior.

Tier 2: Abstract University Billing Interface (Lab Test Standard)

  • Problem Statement: Sourced from KUET Final Laboratory Evaluations. Build a database that handles university personnel records. Design an abstract base class Personnel with a pure virtual function to calculate salary, and implement custom calculations for teaching and non-teaching staff.
  • Class Blueprint & Constraints:
    • Abstract Base Class Personnel:
      • protected: string name;
      • public:
        • Personnel(string n) : name(n) {}
        • virtual float calculateSalary() = 0;: Pure virtual function [v2_poly].
        • virtual ~Personnel(): Virtual destructor to prevent heap leaks [v2_poly].
    • Class AcademicStaff (inherits publicly from Personnel):
      • private: int lectureCount;
      • public: float calculateSalary() override (Formula: ).
    • Class AdministrativeStaff (inherits publicly from Personnel):
      • private: float flatSalary;
      • public: float calculateSalary() override (Formula: flatSalary).
  • Sample Test Input & Expected Console Output:
    • Input Trace (dynamic pointers in array):
      Personnel* roster[2];
      roster[0] = new AcademicStaff("Tahsin", 12);
      roster[1] = new AdministrativeStaff("Anik", 45000.0);
    • Output:
      Name: Tahsin | Salary: 18000
      Name: Anik | Salary: 45000
  • Common Student Pitfall / Bug to Watch For:
    • Non-Virtual Destructor Memory Leak: Declaring a non-virtual destructor in the base class Personnel. When deleting derived objects through a base pointer (delete roster[i]), the compiler will only execute the base-class destructor. The derived class’s destructor is skipped, leaving any heap-allocated memory inside the child class stranded, resulting in a severe memory leak.

Tier 3: VTable / VPtr Memory Offset Mechanics (Exam Trap)

  • Problem Statement: Draw and describe the memory layouts of a base pointer calling virtual overrides. Quantify the object size increase on a 64-bit compiler when the first virtual function is declared.
  • VTable Offset Representation:
    HEAP OBJECT SPACE                   STATIC READ-ONLY MEMORY (VTABLE)
    ┌───────────────────────┐           ┌────────────────────────────────┐
    │  Circle Object        │           │  Circle Class VTable           │
    │  - vptr ──────────────┼──────────>│  - index 0: &Circle::draw()    │
    │  - radius: 4.5        │           │  - index 1: &Shape::resize()   │
    └───────────────────────┘           └────────────────────────────────┘
  • Mechanical Proof:
    • When a class contains a virtual function, the compiler inserts a hidden pointer called vptr (virtual pointer) into the object’s instance memory.
    • On a 64-bit architecture, pointers occupy exactly 8 bytes. Therefore, introducing the first virtual function increases the object’s instance size by 8 bytes (verified using sizeof(Object)).
    • The virtual method table (vtable) itself is allocated only once per class in static, read-only memory, not inside individual objects. The objects store only the vptr.

🏷️ Topic 8 (Week 13 - File Streams & Output Formatting)

Tier 1: Simultaneous File Mode Combiner (Warm-Up)

  • Problem Statement: Write a program that opens a single database file simultaneously for reading and writing in binary mode using file stream flags.
  • Class Blueprint & Constraints: Use fstream combined with bitwise OR mode options: ios::in | ios::out | ios::binary.
  • Sample Test Input & Expected Console Output:
    • Trace: Open “database.dat”, test if successful.
    • Output: Database ready for simultaneous reading and writing.
  • Common Student Pitfall / Bug to Watch For:
    • Truncation Trap: Using ios::out alone on an existing file. This truncates the file’s contents to zero bytes immediately upon opening. To modify or read existing database records, you must include ios::in or ios::app.

Tier 2: Dynamic Ledger Output Aligning (Lab Test Standard)

  • Problem Statement: Sourced from KUET Final Lab Evaluations. Write a program that processes financial records. Print the output in a clean, tabular format on the console using precise manipulators. Ensure columns align perfectly.
  • Class Blueprint & Constraints:
    • Private Data Members: int transactionID, double amount
    • Public Member Functions:
      • void set(int id, double amt)
      • void printRow() const: Prints columns aligned using <iomanip> manipulators.
    • Formatting Requirements:
      • Transaction ID: occupy 8 columns, left-justified, padded with . characters.
      • Amount: occupy 12 columns, right-justified, locked to exactly 2 decimal places.
  • Sample Test Input & Expected Console Output:
    • Input Trace:
      LedgerRow r1, r2;
      r1.set(101, 12500.456);
      r2.set(102, 45.0);
    • Output:
      101.....    12500.46
      102.....       45.00
  • Common Student Pitfall / Bug to Watch For:
    • The setw Reversion Trap: Believing that setw(width) is a persistent setting. Unlike formatting flags (like fixed or setfill), setw applies ONLY to the single next item printed, and immediately deactivates. You must call setw explicitly before every element printed.

Tier 3: Text vs Binary Stream Positioning Pointer Hazard (Exam Trap)

  • Problem Statement: Design a program that writes an array of integers to a file, seeks to the 3rd index, and reads the value back. Explain why relative seek offset movements (e.g., seekg(-15, ios::cur)) on files opened in text-mode constitute undefined behavior under the C++ standard.
  • Class Blueprint & Constraints: Use fstream combined with binary formatting. Show the use of seekg and tellg to perform random access byte jumps.
  • The Text-Mode Positioning Trap:
    • In text mode, C++ streams translate carriage-returns and line-feeds (\r\n to \n on Windows).
    • This translation breaks the direct 1:1 mapping between byte counts and character counts.
    • Consequently, relative seeks with non-zero offsets on text-mode streams result in undefined pointer positions and stream corruption.
    • The Solution: You must open files in binary mode (ios::binary) to ensure safe relative seek positioning.
  • Correct Code Implementation:
    #include <iostream>
    #include <fstream>
    using namespace std;
     
    int main() {
        int numbers[4] = {100, 200, 300, 400};
     
        // Open in BINARY mode to ensure safe relative seeking [v2_files]
        fstream file("data.bin", ios::in | ios::out | ios::binary | ios::trunc);
        file.write((char*)&numbers, sizeof(numbers));
     
        // Jump to 3rd integer (index 2) from beginning [v2_files]
        file.seekg(2 * sizeof(int), ios::beg);
        int val;
        file.read((char*)&val, sizeof(int));
        cout << "Seeked Value: " << val << " (Expected: 300)" << endl;
     
        // Relative seek backward by 1 integer (4 bytes) from current position
        file.seekg(-1 * (int)sizeof(int), ios::cur); // Legal on binary streams [v2_files]
        file.read((char*)&val, sizeof(int));
        cout << "Relative Backtrack Value: " << val << " (Expected: 300)" << endl;
     
        file.close();
        return 0;
    }

⚡ Bonus Section: 10 “Predict the Output” Rapid-Fire Code Snippets

1. The setw One-Shot Output Trap

#include <iostream>
#include <iomanip>
using namespace std;
 
int main() {
    cout << setw(5) << setfill('#') << 99 << 12 << endl;
    return 0;
}
  • Predicted Output: ###9912
  • Technical Explanation: setfill('#') is persistent, but setw(5) applies only to the first output 99, padding it to 5 characters (###99). The second output 12 is printed immediately afterward with default width (no padding).

2. Constructor Execution Order with Inits

#include <iostream>
using namespace std;
 
class Base {
public:
    Base(int x) { cout << "Base: " << x << endl; }
};
 
class Derived : public Base {
public:
    Derived() : Base(5) { cout << "Derived" << endl; }
};
 
int main() {
    Derived d;
    return 0;
}
  • Predicted Output:
    Base: 5
    Derived
  • Technical Explanation: Base constructors execute before derived constructors. The derived class initializer list explicitly passes 5 up to Base(int).

3. Object Slicing with Base Pointer

#include <iostream>
using namespace std;
 
class Base {
public:
    virtual void show() { cout << "Base" << endl; }
};
 
class Derived : public Base {
public:
    void show() override { cout << "Derived" << endl; }
};
 
int main() {
    Base b = Derived(); // Object Slicing!
    b.show();
    return 0;
}
  • Predicted Output: Base
  • Technical Explanation: Assigning a Derived object to a Base object by value slices off the derived properties. Since the object type is statically Base, calling show() invokes the base class implementation. Dynamic polymorphism requires pointers or references.

4. Postfix Overload Evaluation

#include <iostream>
using namespace std;
 
class Count {
public:
    int val;
    Count(int v) : val(v) {}
    Count operator++(int) {
        Count temp = *this;
        val += 5;
        return temp;
    }
};
 
int main() {
    Count c(10);
    Count next = c++;
    cout << next.val << " " << c.val << endl;
    return 0;
}
  • Predicted Output: 10 15
  • Technical Explanation: Postfix increment overloads return a copy of the object’s original state (temp containing 10) before updating the internal variable val to 15.

5. Static Method Execution Context

#include <iostream>
using namespace std;
 
class StaticTest {
public:
    static int num;
    void print() { cout << "Non-static: " << num++ << endl; }
    static void sprint() { cout << "Static: " << num << endl; }
};
int StaticTest::num = 5;
 
int main() {
    StaticTest t;
    t.print();
    StaticTest::sprint();
    return 0;
}
  • Predicted Output:
    Non-static: 5
    Static: 6
  • Technical Explanation: Non-static member functions can access and modify static members. print() prints 5 and increments num to 6. sprint() subsequently prints the updated value.

6. The Copy Assignment Self-Assignment Check

#include <iostream>
using namespace std;
 
class Tracker {
public:
    int id;
    Tracker(int i) : id(i) {}
    Tracker& operator=(const Tracker& rhs) {
        if (this == &rhs) {
            cout << "Self detected" << endl;
            return *this;
        }
        id = rhs.id;
        return *this;
    }
};
 
int main() {
    Tracker t1(1);
    t1 = t1;
    return 0;
}
  • Predicted Output: Self detected
  • Technical Explanation: The copy assignment operator compares the invoking object’s address (this) with the argument’s address (&rhs), successfully detecting and guarding against self-assignment.

7. Virtual Destructor Trace

#include <iostream>
using namespace std;
 
class Base {
public:
    virtual ~Base() { cout << "Base destroyed" << endl; }
};
 
class Derived : public Base {
public:
    ~Derived() { cout << "Derived destroyed" << endl; }
};
 
int main() {
    Base* ptr = new Derived();
    delete ptr;
    return 0;
}
  • Predicted Output:
    Derived destroyed
    Base destroyed
  • Technical Explanation: Because the base destructor is marked virtual, deleting the object through a base pointer invokes dynamic dispatch. The derived destructor executes first, followed automatically by the base destructor.

8. Operator Overloading Parameter Mismatch

#include <iostream>
using namespace std;
 
class Number {
public:
    int n;
    Number(int val) : n(val) {}
    friend int operator+(const Number& lhs, const Number& rhs) {
        return lhs.n + rhs.n;
    }
};
 
int main() {
    Number n1(10), n2(20);
    cout << n1 + n2 << endl;
    return 0;
}
  • Predicted Output: 30
  • Technical Explanation: This binary operator is overloaded as a global friend function, which explicitly accepts exactly two parameters. It evaluates successfully.

9. Diamond Duplication Error (Without Virtual Base)

#include <iostream>
using namespace std;
 
class GP {
public:
    int val = 100;
};
class ParentA : public GP {};
class ParentB : public GP {};
class Child : public ParentA, public ParentB {};
 
int main() {
    Child c;
    // cout << c.val << endl; // COMPILE ERROR: Request for member 'val' is ambiguous
    cout << c.ParentA::val << endl; // Fully Qualified Access
    return 0;
}
  • Predicted Output: 100
  • Technical Explanation: Without virtual base inheritance, Child inherits two separate copies of GP. Direct access via c.val causes a compilation ambiguity error, but scope qualification (c.ParentA::val) resolves it.

10. Unbuffered Error Stream (cerr) Output Mechanics

#include <iostream>
using namespace std;
 
int main() {
    cerr << "Error message ";
    cout << "Normal message ";
    return 0;
}
  • Predicted Output: Error message Normal message (order can sometimes vary depending on the environment, but cerr displays first on standard consoles).
  • Technical Explanation: std::cerr is unbuffered, meaning it bypasses intermediate system buffer queues and prints characters immediately to the output console. std::cout is buffered and can delay rendering.

This Practice Sheet is Complete & Exam Ready.