📚 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
Temperaturethat 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
celsiusis allowed inmain(). Use stream-based I/O (cinandcout) exclusively. Do not use C-style formatting.
- Private Data Members:
- Sample Test Input & Expected Console Output:
- Input:
32.5 - Output:
Recorded Temperature: 32.5°C
- Input:
- 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 beforemain(). - Stream Operator Reversal: Writing
cin << temp;orcout >> temp;. Remember that the extraction operator>>points away from the stream object, whereas the insertion operator<<points towards the stream.
- Missing Class Semicolon: Forgetting the semicolon
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 rollNostring nameint 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.
- Private Data Members:
- 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
- Input:
- Common Student Pitfall / Bug to Watch For:
- The
getlineBuffer Trap: When reading an integer withcin >> rollNofollowed by a string usinggetline(cin, name), the trailing newline character\nremains in the stream buffer. The subsequentgetlineimmediately consumes this newline as an empty string. You must usecin.ignore()to flush the newline before callinggetline.
- The
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:
- Struct default visibility violation: In C++, a
structdefaults topublicaccess, but private blocks can still be declared. However, inmain(),p.age = 20fails becauseageis declared under aprivatelabel. - Class default visibility violation: Members of a
classareprivateby default. Thus,void setRoll(int r)inclass Studentis private. Attempting to calls.setRoll(101)inmain()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; }
- Struct default visibility violation: In C++, a
🏷️ Topic 2 (Week 3 - Member Functions & Arrays of Objects)
Tier 1: Geometry Inline Solver (Warm-Up)
- Problem Statement: Implement a
Rectangleclass 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 theinlinekeyword.
- Private Data Members:
- Sample Test Input & Expected Console Output:
- Input:
5.0 4.0 - Output:
Area: 20
- Input:
- Common Student Pitfall / Bug to Watch For:
- Scope Operator Omission: Writing
void setDimensions(float l, float w) { ... }outside the class without qualifying it asvoid Rectangle::setDimensions(...). The compiler treats this as a global function rather than a member function, causing a compilation error due to undefined variables (lengthandwidth).
- Scope Operator Omission: Writing
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 bookIDchar availability// ‘A’ = Available, ‘I’ = Issued
- Public Data Members:
string titlestring 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.
- Private Data Members:
- 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
- Input:
- 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).
- 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:
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()insidemain()fails to compile.findMax()is aprivatemember function. Private helper functions can be nested (called directly inside public members likedisplayMax()without an object identifier), but they are strictly hidden from external scopes likemain(). - Corrected Code: Remove the unauthorized external call
ns.findMax()inmain().
- Error: The statement
🏷️ Topic 3 (Week 5 - Object Lifecycle & The Rule of Three/Four)
Tier 1: Value Reset Constructor (Warm-Up)
- Problem Statement: Design an
Itemclass 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
- Private Data Members:
- 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 nameditem1that takes no arguments and returns anItemobject. Declare it asItem item1;without parentheses.
- Most Vexing Parse: Declaring a default object as
Tier 2: Dynamic Integer Sequence Manager (Lab Test Standard)
- Problem Statement: Create a class named
DynamicArraythat 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* dataint 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
- Private Data Members:
- 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
- Input Trace (in
- 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
a2shallow-copiesa1, both point to the same heap address. When one goes out of scope and callsdelete[] data, the other pointer is left dangling. When the second object is destroyed, a catastrophic double-free runtime crash occurs.
- 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
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). Whenh2 = h1is compiled, C++ attempts to copyh1into the parametersource. 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
constfor safety:Holder(const Holder& source).
- The Trap: The copy constructor is declared with its parameter passed by value:
🏷️ Topic 4 (Week 7 - Inheritance & Visibility Modes)
Tier 1: Single Public Employee (Warm-Up)
- Problem Statement: Declare a base class
Employeewith protected variables and derive a classManagerpublicly. - 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
- Base Class (
- Sample Test Input & Expected Console Output:
- Input:
Alice 101 "Operations" - Output:
ID: 101 | Name: Alice | Department: Operations
- Input:
- Common Student Pitfall / Bug to Watch For:
- Private Inheritance Access Error: Writing
class Manager : Employeewithout specifyingpublic. In C++, the default inheritance mode for classes isprivate. This converts all public base members to private in the derived class, preventingmain()from accessingsetBase()on aManagerobject.
- Private Inheritance Access Error: Writing
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 classTeacher, and a derived classProfessor. 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 fromStaff):protected: string subject;public: Teacher(int id, string n, string sub): Parameterized constructor.
- Class
Professor(inherits publicly fromTeacher):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 resolutionTeacher::display()to reuse code.
- Class
- Sample Test Input & Expected Console Output:
- Input:
1202 "Dr. Sadik" "Algorithms" "Machine Learning" - Output:
ID: 1202 | Name: Dr. Sadik | Subject: Algorithms | Research: Machine Learning
- Input:
- 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. IfStaffonly defines a parameterized constructor, compilation fails with:no matching function for call to 'Staff::Staff()'.
- 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 (
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
- Constructor Order: Base constructors are executed first, layer-by-layer, to ensure that parent components are initialized before derived classes build upon them. Sequence:
🏷️ Topic 5 (Week 7/8 - Advanced Inheritance, Diamond Problem & Friends)
Tier 1: Multiple Parent Ambiguity Resolver (Warm-Up)
- Problem Statement: Design a class
Derivedthat inherits from two base classesParentAandParentBsimultaneously. Show how to resolve naming conflicts when both parents implement a function namedprint(). - Class Blueprint & Constraints:
- Class
ParentA:public: void print() { cout << "A"; } - Class
ParentB:public: void print() { cout << "B"; } - Class
Derived(inherits publicly from bothParentAandParentB):public: void resolveAndPrint(): Calls both parents’printfunctions.
- Class
- Sample Test Input & Expected Console Output:
- Input:
derivedObj.resolveAndPrint(); - Output:
A and B
- Input:
- Common Student Pitfall / Bug to Watch For:
- Unresolved Ambiguity: Directly writing
print();insideDerived. 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()andParentB::print().
- Unresolved Ambiguity: Directly writing
Tier 2: Same-Class Friend Account Comparator (Lab Test Standard)
- Problem Statement: Sourced from Lab Test 01 - ECE 2k23. Create a class
Accountto 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 depositorIDfloat balance
- Public Data Members:
long acc_nochar type// ‘S’ = Savings, ‘C’ = Currentstring 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.
- Private Data Members:
- 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)
- Input:
- 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 withoutAccount::or thefriendkeyword in its implementation. - The “Both if Same” Return Paradox: If the exam asks you to return “both if same”, a function returning a single
Accountscalar object cannot satisfy this. The industry standard is to return the first object (a1) and log a note detailing the syntactical constraint.
- Scope Resolution Friend Trap: Defining the global friend function outside the class with the class scope resolution prefix (e.g.,
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 fromPerson):class Student : virtual public Person - Intermediate
Sports(inherits virtual publicly fromPerson):class Sports : virtual public Person - Derived
Result(inherits publicly fromStudentandSports)
- Grandparent
- Memory layout diagrams & Mechanics:
- Without Virtual Inheritance:
Resultcontains two disjoint copies ofPerson(viaStudentandSports).Result::nameis ambiguous. - With Virtual Inheritance: The compiler strips the base class out of the normal layout, placing a single shared
Personsubobject at the end of the memory block. It inserts a hidden virtual table offset pointer (vptroffset) insideStudentandSportssubobjects 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 athispointer and cannot accessname.
- Without Virtual Inheritance:
- 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 tototaland returns the dereferencedthispointer (*this).Calculator& sub(int val): Subtracts fromtotaland returns*this.void printResult() const
- Private Data Members:
- Sample Test Input & Expected Console Output:
- Input Trace:
calc.add(20).sub(5).printResult(); - Output:
Total: 15
- Input Trace:
- Common Student Pitfall / Bug to Watch For:
- Returning by Value in Cascading: Declaring the return type as
Calculatorinstead ofCalculator&(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.
- Returning by Value in Cascading: Declaring the return type as
Tier 2: Streaming Complex Coordinate System (Lab Test Standard)
- Problem Statement: Design a class
Complexto 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.
- Private Data Members:
- Sample Test Input & Expected Console Output:
- Input Trace:
Enter Real: 4.5 Enter Imaginary: 2.3 - Output:
Coordinates entered: 4.5 + i2.3
- Input Trace:
- 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 anostreamobject, not your custom class object. Since you cannot modify the standardstd::ostreamclass, streaming overloads must be implemented as global friend functions.
- Streaming Member Operator Error: Attempting to overload
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 dummyintparameter. Returns by value.
- Private Data Members:
- 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.
- Prefix (
- 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.
- Base Class
- Sample Test Input & Expected Console Output:
- Trace:
ptr->draw(); - Output:
Circle
- Trace:
- 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’svtable, completely destroying polymorphic behavior.
- Object Slicing: Assigning a derived object to a base object by value (e.g.,
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
Personnelwith 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 fromPersonnel):private: int lectureCount;public: float calculateSalary() override(Formula: ).
- Class
AdministrativeStaff(inherits publicly fromPersonnel):private: float flatSalary;public: float calculateSalary() override(Formula:flatSalary).
- Abstract Base Class
- 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
- Input Trace (dynamic pointers in array):
- 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.
- Non-Virtual Destructor Memory Leak: Declaring a non-virtual destructor in the base class
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 thevptr.
- When a class contains a virtual function, the compiler inserts a hidden pointer called
🏷️ 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
fstreamcombined 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::outalone 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 includeios::inorios::app.
- Truncation Trap: Using
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.
- Transaction ID: occupy 8 columns, left-justified, padded with
- Private Data Members:
- 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
- Input Trace:
- Common Student Pitfall / Bug to Watch For:
- The
setwReversion Trap: Believing thatsetw(width)is a persistent setting. Unlike formatting flags (likefixedorsetfill),setwapplies ONLY to the single next item printed, and immediately deactivates. You must callsetwexplicitly before every element printed.
- The
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
fstreamcombined with binary formatting. Show the use ofseekgandtellgto perform random access byte jumps. - The Text-Mode Positioning Trap:
- In text mode, C++ streams translate carriage-returns and line-feeds (
\r\nto\non 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.
- In text mode, C++ streams translate carriage-returns and line-feeds (
- 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, butsetw(5)applies only to the first output99, padding it to 5 characters (###99). The second output12is 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
5up toBase(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
Derivedobject to aBaseobject by value slices off the derived properties. Since the object type is staticallyBase, callingshow()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 (
tempcontaining10) before updating the internal variablevalto15.
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()prints5and incrementsnumto6.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
virtualbase inheritance,Childinherits two separate copies ofGP. Direct access viac.valcauses 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, butcerrdisplays first on standard consoles). - Technical Explanation:
std::cerris unbuffered, meaning it bypasses intermediate system buffer queues and prints characters immediately to the output console.std::coutis buffered and can delay rendering.
This Practice Sheet is Complete & Exam Ready.