optional · CSS study notes
🔥 Most Repeated — Object Oriented Paradigm
CSS Computer Science (Optional) — Paper-I, Section-A, Topic III. These 9 patterns recur across FPSC past papers (2016, 2018, 2020). The "design a system using OOP" format is the single most reliable question type in this topic — it appears in some form almost every year, just with a different real-world scenario (university, library, medical store, student registration).
Q1. OOP System Design — Student Registration System
Appeared: 2016, Q2(c)
Design a Student Registration System using OOP. Define classes including Person, Teacher, Student, Course, Section, Allocation, and Registration — each with
getdata(),showdata(), and suitable constructors.
Solution (class list and relationships):
| Class | Relationship | Why |
|---|---|---|
| Person (base) | — | Shared attributes: name, age, CNIC |
| Teacher : Person, Student : Person | is-a (inheritance) | Both are specialized kinds of Person |
| Allocation | has-a Course + Teacher + Section | Records which teacher teaches which course, in which section |
| Registration | has-a Student + Course + result data | Records which student is enrolled in which course, with grade |
This is a full worked question with complete, compilable C++ code (including virtual functions and a polymorphic vector<Person*> roster) already on the Classes and Objects — Exam Practice page — see "Exam Practice: Student Registration System" there for the full listing. The University Records System variant on the same page (file-based, no virtual functions) shows the contrasting case where polymorphism must deliberately be avoided.
Q2. OOP System Design — Library Information System (design only, no code)
Appeared: 2016, Q3(c)
Design a Library Information System using OOP. Describe classes (Student, Teacher, Book, Issue Book, etc.) with appropriate data members — no code implementation required.
Solution:
| Class | Key Attributes | Relationship |
|---|---|---|
| Member (base) | MemberID, Name, ContactInfo | Shared by Student and Teacher members |
| Student : Member | RollNo, Program, MaxBooksAllowed (e.g. 3) | is-a Member, with a lower borrowing limit |
| Teacher : Member | EmployeeID, Department, MaxBooksAllowed (e.g. 10) | is-a Member, with a higher borrowing limit |
| Book | ISBN, Title, Author, CopiesAvailable | Standalone entity catalogued by the library |
| IssueRecord | Member (reference), Book (reference), IssueDate, DueDate, ReturnDate | has-a Member + has-a Book — the linking/junction class |
Key reasoning points: since the question explicitly says "no code implementation required," the marks are for correctly identifying (a) which classes should share a common base (Student/Teacher → Member, to avoid duplicating MemberID/Name), (b) which classes are linked via composition rather than inheritance (IssueRecord is not "a kind of" Book or Member — it has both), and (c) realistic, sufficient attributes per class (3–5 is the expected depth). Writing full C++ code when a question says "describe" wastes exam time without earning extra marks.
Q3. OOP System Design — Medical Store System (C++/Java code required)
Appeared: 2016, Q4(c)
Design Medical Store software with classes Person, Customer, Salesman, Purchase (with attributes), using C++/Java syntax, applying inheritance where appropriate.
Solution:
#include <iostream>
#include <string>
using namespace std;
class Person
{
protected:
string name;
int age;
string contact;
public:
Person() : name(""), age(0), contact("") {}
virtual void getdata()
{
cout << "Enter name: "; cin >> name;
cout << "Enter age: "; cin >> age;
cout << "Enter contact: "; cin >> contact;
}
virtual void showdata() const
{
cout << "Name: " << name << ", Age: " << age << ", Contact: " << contact << endl;
}
virtual ~Person() {}
};
class Customer : public Person
{
private:
string customerId;
public:
void getdata() override
{
Person::getdata();
cout << "Enter Customer ID: "; cin >> customerId;
}
void showdata() const override
{
Person::showdata();
cout << "Customer ID: " << customerId << endl;
}
};
class Salesman : public Person
{
private:
string employeeId;
double commissionRate;
public:
void getdata() override
{
Person::getdata();
cout << "Enter Employee ID: "; cin >> employeeId;
cout << "Enter Commission Rate: "; cin >> commissionRate;
}
void showdata() const override
{
Person::showdata();
cout << "Employee ID: " << employeeId << ", Commission Rate: " << commissionRate << "%" << endl;
}
};
class Purchase
{
private:
string medicineName;
int quantity;
double unitPrice;
Customer buyer; // has-a Customer
Salesman servedBy; // has-a Salesman
public:
void getdata()
{
cout << "-- Buyer --\n"; buyer.getdata();
cout << "-- Salesman --\n"; servedBy.getdata();
cout << "Enter medicine name: "; cin >> medicineName;
cout << "Enter quantity: "; cin >> quantity;
cout << "Enter unit price: "; cin >> unitPrice;
}
void showdata() const
{
buyer.showdata();
servedBy.showdata();
cout << "Medicine: " << medicineName << ", Qty: " << quantity
<< ", Total: " << (quantity * unitPrice) << endl;
}
};
Key reasoning points: Customer and Salesman both inherit from Person (is-a — they share name/age/contact), while Purchase composes a Customer and a Salesman (has-a — a purchase transaction is not a kind of person, it involves two people plus transaction-specific data). This is the same inheritance-vs-composition judgment call tested in nearly every "design a system" question on this paper — get it right and the rest of the answer follows naturally.
Q4. Protected Members in Inheritance Hierarchies
Appeared: 2018, Q4(a)
Where should protected members be placed in an inheritance hierarchy, and why?
Solution:
class Base
{
protected:
int value; // accessible inside Base AND inside any derived class -- not from outside either
};
class Derived : public Base
{
public:
void setValue(int v) { value = v; } // OK -- protected members are visible to derived classes
};
int main()
{
Derived d;
// d.value = 5; // ERROR -- not accessible from outside the class hierarchy
d.setValue(5); // OK -- through a public method
}
Key reasoning points: protected sits between private (class-only) and public (everywhere) — place a member as protected in the base class precisely when derived classes need direct access to it but the outside world should not. It should never be used just to "be safe" — if no derived class needs direct access, private with accessor methods is the stronger encapsulation choice.
Q5. Private Member Access Across Classes
Appeared: 2018, Q4(b)
Can a private member be accessed from a derived class or another class? Explain.
Solution:
class Account
{
private:
double balance;
public:
Account() : balance(0) {}
double getBalance() const { return balance; } // controlled access point
};
class SavingsAccount : public Account
{
public:
void show()
{
// cout << balance; // ERROR -- private members of Account are NOT accessible here, even though SavingsAccount inherits from it
cout << getBalance(); // OK -- accessed only through Account's own public method
}
};
Key reasoning points: private means accessible only within the class that declares it — not even a derived class can reach it directly, which is exactly what distinguishes private from protected. The only sanctioned way around this is a friend declaration (an explicit, named exception the class itself grants) or public/protected accessor methods — inheritance alone never grants access to private members.
Q6. Abstract Class vs Concrete Class (Pure Virtual Functions)
Appeared: 2018, Q4(c)
Distinguish between abstract and concrete classes, using pure virtual functions.
Solution:
class Shape // abstract class
{
public:
virtual double area() const = 0; // pure virtual -- "= 0" means no implementation here
virtual ~Shape() {}
};
class Circle : public Shape // concrete class
{
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override { return 3.14159 * radius * radius; } // implements the pure virtual
};
int main()
{
// Shape s; // ERROR -- cannot instantiate an abstract class
Circle c(5); // OK -- Circle is concrete, all pure virtuals are implemented
Shape* ptr = &c; // OK to use a Shape pointer/reference to a concrete derived object
}
Key reasoning points: a class becomes abstract the moment it declares at least one pure virtual function (= 0) — it can never be instantiated directly, only used as a base/interface. A concrete class is any class where every inherited pure virtual has been overridden with a real implementation, making it safe to instantiate.
Q7. is-a vs has-a: Inheritance vs Composition (Vehicle Example)
Appeared: 2018, Q4(d)
Build a relationship table mapping is-a and has-a associations among vehicle-related classes.
Solution:
| Classes | Relationship | Reasoning |
|---|---|---|
| Car : Vehicle, Truck : Vehicle | is-a (inheritance) | A Car IS A (specialized kind of) Vehicle |
| Vehicle has-a Engine | has-a (composition) | A Vehicle HAS AN Engine, but is not itself a kind of Engine |
| Car has-a SteeringWheel, Wheel[4] | has-a (composition) | Parts that make up a Car, not types of Car |
class Engine { /* horsepower, fuelType, ... */ };
class Vehicle
{
protected:
Engine engine; // has-a -- Vehicle is composed of an Engine
string registrationNo;
};
class Car : public Vehicle { /* is-a Vehicle, adds Car-specific attributes */ };
class Truck : public Vehicle { double loadCapacity; /* is-a Vehicle */ };
Key reasoning points: the quick test is to say the relationship out loud — "a Car is a Vehicle" reads naturally (inheritance), while "a Vehicle is an Engine" does not (it should be "a Vehicle has an Engine", i.e. composition). Getting this test right is the single most reused piece of judgment across every OOP system-design question on these papers.
Q8. How the OOP Paradigm Relates to Real-World Problems
Appeared: 2020, Q4(a)
How can the OOP paradigm be associated with real-world problems? Explain.
Solution:
OOP models software the way people naturally think about the real world: nouns become classes (Student, Book, Account) and verbs become methods (enroll(), borrow(), withdraw()). The four pillars map directly onto everyday real-world structure:
- Encapsulation: a bank account bundles balance + the rules for changing it (deposit/withdraw) together, just like a real account doesn't let anyone directly edit the ledger
- Abstraction: a driver uses a car's pedals and wheel without knowing engine internals — an object's public interface hides its internal complexity the same way
- Inheritance: a "Manager" is a specialized kind of "Employee" with everything an Employee has, plus more — mirroring real organizational hierarchies
- Polymorphism: "make a sound" means something different for a Dog, Cat, or Bird, yet all can be commanded uniformly — the same real-world idea of one instruction, many appropriate responses
Benefits that follow directly from this mapping: code reusability (inheritance), data security (encapsulation), reduced complexity for the programmer (abstraction), and flexibility/extensibility (polymorphism) — this list of four benefits is itself a common standalone short-answer question.
Q9. Criticisms of / Reasons Against the OOP Paradigm
Appeared: 2020, Q4(b)
Discuss critical reasons given by professionals for not supporting the OOP paradigm.
Solution:
- Performance overhead: virtual function calls require a vtable lookup at runtime instead of a direct call, and objects carry extra memory overhead (e.g. a hidden vtable pointer) — costly in performance-critical or embedded systems.
- Steeper learning curve: correctly designing class hierarchies, choosing between inheritance and composition, and understanding polymorphism take longer to master than straightforward procedural code.
- Over-engineering risk: it's easy to build unnecessarily deep or complex class hierarchies for problems that don't actually need them, adding indirection without real benefit.
- Not a natural fit for every problem: purely mathematical/computational tasks (numerical algorithms, simple scripts) are often clearer and faster to write procedurally; forcing an OOP structure onto them adds ceremony without value.
- State scattered across objects: with data and behavior distributed across many interacting objects, tracing how and when state changes (debugging) can be harder than in a single linear procedural flow.
Exam angle: a strong answer doesn't just list criticisms — it shows balance by noting these are trade-offs, not universal flaws (e.g., the same encapsulation that adds overhead is what makes large codebases maintainable), which is what Q4(a) and Q4(b) together are really testing: can the candidate argue both sides of the same paradigm.
Q10. Why an Abstract Class Cannot Be Instantiated
Appeared: 2026, Q4(a)
Why cannot we instantiate an abstract class directly, even though it may contain complete functions?
Solution:
An abstract class contains at least one pure virtual function (= 0), which has no implementation of its own — it is a contract that derived classes must fulfill. Even if the class also has other, fully-implemented (complete) functions, the compiler cannot build a complete object of that exact type, because calling the still-unimplemented pure virtual member on it would have no defined behavior. Since instantiation requires every member function to have a real implementation somewhere in the object's own class or its bases, the compiler simply forbids creating an object of an abstract class — it can only be used as a base/interface; only a concrete derived class that overrides every pure virtual function can be instantiated.
class Shape
{
public:
virtual double area() const = 0; // pure virtual -- no implementation
void printLabel() { cout << "Shape\n"; } // complete function -- doesn't matter, class is still abstract
};
// Shape s; // ERROR: cannot instantiate an abstract class
Q11. Composition vs Inheritance — a Library of Book Objects
Appeared: 2026, Q4(b)
A class library contains many book objects. Should this be modeled using inheritance or composition? Why?
Solution: Composition ("has-a"), not inheritance.
A Library has (contains/owns) many Book objects; a Library is not itself a specialized kind of Book — it fails the natural "is-a" test ("a Library is a Book" doesn't make sense). Model it as:
class Library
{
private:
vector<Book> books; // has-a -- Library owns a collection of Book objects
public:
void addBook(const Book& b) { books.push_back(b); }
};
Key reasoning points: inheritance models an "is-a" specialization relationship; composition/aggregation models a "has-a"/ownership/collection relationship. The moment a class merely holds or manages many instances of another class as data, it is composition — never inheritance. This is the same is-a/has-a judgment tested throughout the "design a system" question family above.
Q12. Method Overriding Demo — Account / CurrentAccount (Protected Balance)
Appeared: 2026, Q4(c)
Demonstrate method overriding using a base class Account and a derived class Current Account. Make balance a protected data member.
Solution:
#include <iostream>
using namespace std;
class Account
{
protected:
double balance; // protected -- accessible directly to derived classes
public:
Account(double b) : balance(b) {}
virtual void showBalance() const // virtual -- enables true overriding
{
cout << "Account Balance: " << balance << endl;
}
};
class CurrentAccount : public Account
{
private:
double overdraftLimit;
public:
CurrentAccount(double b, double odl) : Account(b), overdraftLimit(odl) {}
void showBalance() const override // overrides the base version
{
cout << "Current Account Balance: " << balance
<< " (Overdraft Limit: " << overdraftLimit << ")" << endl;
}
};
int main()
{
Account* acc = new CurrentAccount(5000, 2000);
acc->showBalance(); // calls CurrentAccount's version -- runtime polymorphism
delete acc;
return 0;
}
Key reasoning points: showBalance() must be declared virtual in the base class for true overriding/dynamic dispatch through a base-class pointer — omit it and acc->showBalance() would incorrectly call Account's version. balance is protected so CurrentAccount can read it directly without needing an accessor. This single question packages three separate concepts examiners test constantly: protected access, method overriding, and calling the overridden version polymorphically through a base pointer.
Frequency Note
Papers reviewed: 2016, 2018, 2020, 2026. The "design a system" format (Q1–Q3) and the abstract-class/composition/overriding theory cluster (Q4, Q6, Q7, Q10–Q12) are the two most reliable patterns — some version of "design classes for X" or "distinguish is-a from has-a" appears in nearly every year reviewed. See the full 22-page concept breakdown starting at P-I.III. Object Oriented Paradigm for deeper coverage of any single concept (inheritance forms, polymorphism, STL, file streams, exception handling).
2026 Past Paper MCQs (Part-I)
-
A class needs to allow different implementations of a method. What concept to use?
A) Inheritance B) Polymorphism C) Encapsulation D) None of these
Why: specifically method overriding — each derived class supplies its own implementation of the same interface method.
-
What is the purpose of using virtual functions in inheritance?
A) To prevent overriding B) To hide members C) To enable polymorphism D) None of these
Why: declaring a base-class function
virtualis exactly what allows a call through a base pointer/reference to resolve to the derived class's overridden version at runtime. -
A developer wants to define a class with methods that can be used with different data types. What to use?
A) Templates B) Inheritance C) Polymorphism D) None of these
Why: C++ templates let one class/function definition work generically across any data type, resolved at compile time — distinct from polymorphism, which resolves at runtime across related types in an inheritance hierarchy.
-
Which operator is typically overloaded for stream insertion?
A) << B) >> C) + D) None of these
Why:
operator<<is overloaded (as afriendfunction, sincecoutis the left operand) to define how an object prints viacout <<. -
What does the Standard Template Library (STL) provide in C++?
A) Pre-defined classes for GUI B) Pre-defined functions for math operations C) Pre-defined containers and algorithms D) None of these
Why: vectors, lists, maps, and sets, plus algorithms like sort/find — all templated to work across data types.