CSS Prepare

optional · CSS study notes

1. Object Oriented Programming Concepts

Why OOP Exists

Procedural languages (C) tie data and the functions that act on it loosely together — global data can be changed by any function, anywhere, making large programs hard to maintain and reason about. OOP fixes this by binding data and behavior into a single unit (the class), controlling exactly who can touch what, and modeling programs as a set of interacting objects rather than a sequence of instructions.

Classification / Structure of OOP Concepts (Big Picture)

Use this as the mental map before drilling into definitions — CSS questions often ask you to "classify" or "categorize" OOP concepts, not just define them.

  • A. Basic Building Blocks
    • Object, Class, Attribute (data member), Method (member function), Message Passing
  • B. The Four Pillars (Core Principles)
    • Abstraction, Encapsulation, Inheritance, Polymorphism
  • C. Object Relationships (how classes relate to each other)
    • Association (general "uses-a" link) → Aggregation ("has-a," weak, parts can exist independently) → Composition ("has-a," strong, parts die with the whole) → Inheritance ("is-a")
  • D. Binding Types (when a call is resolved)
    • Static/Early Binding — resolved at compile time (e.g., function/operator overloading)
    • Dynamic/Late Binding — resolved at run time (e.g., virtual functions, overriding)
  • E. Design-Quality Metrics (how "good" an OOP design is)
    • Coupling — degree of interdependency between classes (aim: low coupling)
    • Cohesion — how closely related/focused a single class's responsibilities are (aim: high cohesion)

Glossary of Key OOP Terms (Definitions)

  • Object — a runtime instance of a class; a self-contained unit combining state (data) and behavior (methods). Example: myCar is an object of class Car.
  • Class — a blueprint/template that defines the attributes and methods an object of that type will have; no memory is allocated until an object is created from it.
  • Attribute / Data Member — a variable that holds an object's state (e.g., balance in a BankAccount class).
  • Method / Member Function — a function defined inside a class that operates on its data members (e.g., deposit()).
  • Instance — another word for "object"; "instantiation" is the act of creating an object from a class.
  • Message Passing — how objects communicate: one object invokes another object's method, optionally passing data.
  • Constructor — a special member function automatically invoked when an object is created; typically initializes data members. Has the same name as the class, no return type.
  • Destructor — a special member function automatically invoked when an object is destroyed; used to release resources. Prefixed with ~ in C++.
  • Abstraction — exposing only essential features of an object while hiding implementation detail ("what it does," not "how it does it").
  • Encapsulation — bundling data and methods into a single unit (class), typically restricting direct access to internal state via access modifiers.
  • Inheritance — a mechanism where a derived (child) class acquires properties/behavior of a base (parent) class, enabling code reuse and hierarchical classification.
  • Polymorphism — "many forms": the ability of different objects/classes to respond to the same interface/message in type-specific ways.
  • Overloading — defining multiple functions/operators with the same name but different parameter lists, resolved at compile time (a form of static/compile-time polymorphism).
  • Overriding — a derived class providing its own implementation of a method already defined in its base class, resolved at run time (a form of dynamic polymorphism).
  • Virtual Function — a base-class member function declared with the virtual keyword so that a derived class's overriding version is called even through a base-class pointer/reference — the mechanism that enables run-time polymorphism.
  • Pure Virtual Function — a virtual function with no implementation in the base class (virtual void draw() = 0;); forces every derived (concrete) class to provide one.
  • Abstract Class — a class containing at least one pure virtual function; cannot be instantiated directly, only used as a base for derived classes.
  • Interface — a fully abstract type (in C++, typically simulated via a class made entirely of pure virtual functions) specifying "what" a class must do without saying "how."
  • Association — a general relationship where one class uses or interacts with another, without ownership (e.g., a Teacher and a Student in the same School).
  • Aggregation — a weaker "has-a" relationship; the "part" object can exist independently of the "whole" (e.g., a Department has Professors, but professors exist even if the department is dissolved).
  • Composition — a stronger "has-a" relationship; the "part" cannot exist independently of the "whole" — destroying the whole destroys the part (e.g., a House and its Rooms).

For the full side-by-side treatment of Association vs Aggregation vs Composition — comparison table, code for all three, and dedicated MCQs — see the "Object Relationships" section on the "6. Classes Advanced" page.

  • Coupling — the degree to which one class depends on another; low coupling = classes are more independent and easier to maintain/reuse.
  • Cohesion — how strongly the responsibilities within a single class are related; high cohesion = a class does one thing well.
  • Static Binding (Early Binding) — the compiler decides, at compile time, which function to call (e.g., normal function calls, overloading).
  • Dynamic Binding (Late Binding) — the specific function to call is decided at run time, based on the actual object type (e.g., virtual functions).
  • Access Modifiers — keywords controlling visibility of class members: private (class only), protected (class + derived classes), public (accessible everywhere).
  • this Pointer — an implicit pointer available inside every non-static member function, pointing to the object that invoked it.
  • Friend Function — a non-member function granted access to a class's private/protected members by being declared friend inside that class.
  • Static Member — a data member or method that belongs to the class itself (one shared copy for all objects), not to any individual object.

The Four Pillars, One at a Time (with Code)

MCQ Trap: Encapsulation and Data Abstraction are related but distinct: abstraction is about hiding complexity/showing only essentials; encapsulation is the mechanism (bundling + access control) that achieves it.

1. Data Abstraction

Showing only what's necessary, hiding the rest. A driver uses a steering wheel and pedals without knowing the engine's internals.

class Car
{
public:
    void start() { /* complex ignition sequence hidden inside */ }
};
// Car c; c.start();  -- the user only sees start(), not how it works internally

2. Encapsulation

Bundling data + methods, and controlling access.

class BankAccount
{
private:
    double balance;   // hidden -- can't be changed directly from outside
public:
    void deposit(double amt) { if (amt > 0) balance += amt; }   // controlled access
};

3. Inheritance

Reusing and extending an existing class.

class Vehicle { public: void move() { cout << "moving\n"; } };
class Car : public Vehicle { public: void honk() { cout << "beep\n"; } };

4. Polymorphism

Same interface, different behavior depending on the actual object type.

class Shape { public: virtual void draw() { cout << "generic shape\n"; } };
class Circle : public Shape { public: void draw() override { cout << "circle\n"; } };

Important MCQs (with Answers)

  1. Which OOP principle refers to hiding internal implementation details while exposing only necessary features?
    • a) Encapsulation b) Abstraction c) Inheritance d) Polymorphism
  2. Bundling data and the methods that operate on it into a single unit is called:
    • a) Abstraction b) Polymorphism c) Encapsulation d) Overloading
  3. Which mechanism allows a derived class to acquire the properties of a base class?
    • a) Polymorphism b) Inheritance c) Encapsulation d) Abstraction
  4. "One interface, multiple implementations" best describes:
    • a) Encapsulation b) Abstraction c) Polymorphism d) Composition
  5. Function overloading is resolved at:
    • a) Run time b) Compile time c) Load time d) Link time
  6. Virtual functions achieve polymorphism resolved at:
    • a) Compile time b) Run time c) Design time d) None of these
  7. A class containing at least one pure virtual function is called:
    • a) Concrete class b) Abstract class c) Friend class d) Static class
  8. Which relationship represents ownership, where the "part" cannot exist independently of the "whole"?
    • a) Association b) Aggregation c) Composition d) Inheritance
  9. A weaker "has-a" relationship, where the part CAN exist independently of the whole, is called:
    • a) Composition b) Aggregation c) Encapsulation d) Overriding
  10. In C++, an access modifier that allows access within the class and its derived classes, but not from outside, is:
    • a) private b) public c) protected d) static
  11. Which of the following does NOT support multiple inheritance directly through classes?
    • a) C++ b) Python c) Java d) Perl
  12. The special member function automatically called when an object is created is the:
    • a) Destructor b) Constructor c) Friend function d) Static function
  13. Low coupling and high cohesion are generally considered:
    • a) Bad design goals b) Good design goals c) Irrelevant to OOP d) Only relevant to procedural programming
  14. A static member of a class:
    • a) Belongs to each object separately b) Is shared by all objects of the class c) Cannot be accessed by any object d) Is automatically private
  15. Which term describes objects communicating by invoking each other's methods?
    • a) Message passing b) Data hiding c) Operator overloading d) File streaming
  16. A pure virtual function is declared with which syntax in C++?
    • a) virtual void f(); b) void f() = 0; c) virtual void f() = 0; d) static void f();
  17. Which of the following is an example of compile-time (static) polymorphism?
    • a) Virtual functions b) Method overriding c) Operator/function overloading d) Abstract classes

MCQ Traps (Topic-Wide)

  • Encapsulation and Data Abstraction are related but distinct: abstraction hides complexity; encapsulation is the mechanism (bundling + access control) that achieves it.
  • Overloading (same name, different parameters) is resolved at compile time; overriding (redefining a base method in a derived class) is resolved at run time via virtual functions — a very frequently tested pair.
  • Aggregation vs Composition: both are "has-a" relationships, but only composition implies the part cannot outlive the whole — don't treat them as synonyms.
  • Abstract class vs Interface: an abstract class can have a mix of implemented and pure virtual functions; a pure interface (in the C++ sense) has only pure virtual functions and no data.
  • All 4 pillars typically appear together in real code, but each is a separate, independently testable concept — a question can ask about just one.
  • static members belong to the class (one shared copy); non-static (instance) members belong to each object separately.

✅ Self-Test Questions (with Answers)

Q1. Name the four pillars of OOP and give a one-line definition for each.

Ans: The four pillars are Abstraction, Encapsulation, Inheritance and Polymorphism (mnemonic: A-PIE).

  • Abstraction — exposing only an object's essential features and hiding the implementation detail, i.e. showing what it does, not how; achieved in C++ through abstract classes and carefully designed public interfaces.
  • Encapsulation — bundling data and the functions that operate on it into one class and restricting direct access to the data through access specifiers (private, protected, public); the mechanism that delivers data hiding.
  • Inheritance — deriving a new class from an existing one so it reuses and extends the base class's members, expressing an "is-a" relationship and eliminating duplicated code.
  • Polymorphism — "many forms": the same interface produces different behaviour depending on the actual object — compile-time through overloading, run-time through virtual functions and overriding.
class Shape {                              // ABSTRACTION: interface only
    string label;                          // ENCAPSULATION: private data
public:
    explicit Shape(string l) : label(std::move(l)) {}
    string name() const { return label; }
    virtual double area() const = 0;       // pure virtual -> abstract class
    virtual ~Shape() = default;            // virtual dtor in a polymorphic base
};
class Circle : public Shape {              // INHERITANCE: Circle IS-A Shape
    double r;
public:
    Circle(double r) : Shape("Circle"), r(r) {}
    double area() const override { return 3.14159 * r * r; }   // POLYMORPHISM
};
class Square : public Shape {
    double s;
public:
    Square(double s) : Shape("Square"), s(s) {}
    double area() const override { return s * s; }
};

vector<Shape*> v{ new Circle(1), new Square(2) };
for (Shape* p : v) cout << p->name() << ": " << p->area() << '\n';
// Circle: 3.14159
// Square: 4          <- one call site, two behaviours (late binding)

MCQ traps: abstraction and encapsulation are not synonyms (design intent vs mechanism); overloading is compile-time, overriding is run-time; and some syllabi list Message Passing as a fifth characteristic, but the standard four-pillar answer is the one expected unless the question says otherwise.

Q2. Why does binding data and functions together (encapsulation) improve on the procedural approach?

Ans: In procedural code, data structures and the functions that manipulate them are separate; the data is typically global or freely reachable, so any function anywhere can modify it, and nothing in the language enforces the rules that keep it valid. Encapsulation draws a boundary around the data, makes it private, and lets only the class's own member functions cross that boundary. The practical gains are:

  • Data integrity — invariants ("balance is never negative") are enforced in one place, in the setter, instead of being re-checked by every caller.
  • Compile-time safety — illegal access becomes a compiler error at the exact line of misuse rather than a run-time corruption discovered far away.
  • Localised debugging — if a value is wrong, only the handful of member functions that can write it need auditing, not the whole program.
  • Implementation freedom — the internal representation can change without touching client code, because clients only use the public interface.
  • Lower coupling, higher cohesion — each class becomes a self-contained unit that can be tested and reused independently.
// Procedural: the invariant is unenforceable
struct Acc { double balance; };
Acc a{100};
a.balance = -5000;                 // compiles -- corruption, no diagnostic

// Encapsulated: the invariant is enforced ONCE, by the class
class Account {
    double balance;                                  // private
public:
    explicit Account(double b) : balance(b > 0 ? b : 0) {}
    bool deposit(double amt) {
        if (amt <= 0) return false;                  // guard in one place
        balance += amt; return true;
    }
    double getBalance() const { return balance; }    // const-correct read-only access
};
Account b(100);
// b.balance = -5000;              // ERROR: 'balance' is private
b.deposit(-5000);                  // returns false; state remains valid

MCQ trap: encapsulation is not merely "making variables private" — it is bundling the data with the operations allowed on it and controlling access. Also note the related distinction: encapsulation is the mechanism, abstraction is the goal it serves.

Q3. Give a real-world analogy (not from this page) for data abstraction.

Ans: An ATM machine is a clean analogy for data abstraction. As a customer you are given a tiny, well-defined interface — insert card, enter PIN, choose Withdraw, Deposit or Balance Enquiry — and you get a correct result every time. You never see, and never need to see, how the machine authenticates the card against the bank's core system, how it locks the ledger row, how it dispenses notes from the cassettes, or how the transaction is journalled for audit. The essential behaviour is exposed; the implementation is hidden. Crucially, the bank can replace the entire back-end software overnight and your interaction with the ATM is unchanged — that stability of the interface in the face of changing implementation is the real payoff of abstraction.

class ATM {
    // ---- hidden implementation: none of this is the user's concern ----
    bool authenticate(int pin);
    void debitCoreBankingLedger(double amt);
    void dispenseNotes(double amt);
    void writeAuditJournal(double amt);
public:
    // ---- the abstraction: the only thing the customer sees ----
    bool withdraw(int pin, double amt) {
        if (!authenticate(pin)) return false;
        debitCoreBankingLedger(amt);
        dispenseNotes(amt);
        writeAuditJournal(amt);
        return true;
    }
    double balanceEnquiry(int pin);
};
// User writes only:  atm.withdraw(1234, 5000);

Other equally valid analogies: a TV remote (buttons exposed, infrared protocol and circuitry hidden), a mobile phone call (dial a number; the radio handshake and cell-tower handover are invisible), or an electrical switch. MCQ trap: state the distinction from encapsulation if asked — abstraction is the design intent of showing only essentials (achieved in C++ through abstract classes and well-chosen public interfaces), while encapsulation is the mechanism of bundling data with its functions and locking it behind access specifiers.

Q4. Differentiate between association, aggregation, and composition with one example each.

Ans: All three describe how objects relate without inheritance, and they form a chain of increasing strength: association → aggregation → composition. Association is a plain "uses-a" link with no ownership; aggregation is a weak "has-a" where the part can outlive the whole; composition is a strong "has-a" where the part dies with the whole.

AspectAssociationAggregationComposition
Relationshipuses-ahas-a (weak)has-a (strong)
OwnershipNoneShared / noneExclusive
Lifetime of partIndependentIndependent of the wholeEnds with the whole
Typical C++ memberReference or raw pointerPointer or reference to an outside objectValue member, or owning unique_ptr
UML notationPlain lineHollow diamond at the wholeFilled diamond at the whole
ExampleTeacher uses a ProjectorDepartment has ProfessorsHouse has Rooms
// ASSOCIATION -- Doctor merely interacts with a Patient; neither owns the other
class Patient;
class Doctor { public: void treat(Patient& p); };

// AGGREGATION -- Department holds Professors that exist independently of it
class Professor { string name; };
class Department {
    vector<Professor*> staff;      // non-owning: dissolving the dept does NOT
public:                            // destroy the professors
    void add(Professor* p) { staff.push_back(p); }
};

// COMPOSITION -- Rooms are created and destroyed with the House
class Room { public: Room(const string&) {} };
class House {
    Room kitchen{"kitchen"};       // value members: constructed with the House,
    Room bedroom{"bedroom"};       // destroyed with it automatically
};

Memory aid and MCQ trap: aggregation and composition are both "has-a," so a question that only says "has-a" is ambiguous — the discriminator is always lifetime. Kill the Department object and the professors survive (aggregation); demolish the House and its Rooms cease to exist (composition). Also remember that all three are alternatives to inheritance ("is-a"), and modern design guidance is to favour composition over inheritance because it couples classes more loosely and can be re-wired at run time.

Q5. What is the difference between static binding and dynamic binding? Give one example of each.

Ans: Binding is the act of connecting a function call to the actual function body that will run. Static (early) binding does this at compile time using the declared type of the expression; dynamic (late) binding postpones it to run time, choosing the function from the actual type of the object pointed to, through the vtable/vptr mechanism.

AspectStatic / early bindingDynamic / late binding
Resolved atCompile timeRun time
Decided byDeclared (static) type of the pointer or referenceActual type of the object
C++ triggerNormal calls, overloading, templates, non-virtual functionsvirtual function called via base pointer or reference
MechanismDirect call to a fixed addressIndirect call through vptr into the vtable
CostFaster; inlinableOne extra indirection; blocks inlining
FlexibilityRigidEnables run-time polymorphism
class Base {
public:
    void stat()  { cout << "Base::stat\n";  }   // NON-virtual -> early binding
    virtual void dyn() { cout << "Base::dyn\n"; } // virtual   -> late binding
    virtual ~Base() = default;
};
class Derived : public Base {
public:
    void stat() { cout << "Derived::stat\n"; }   // hides, does not override
    void dyn() override { cout << "Derived::dyn\n"; }
};

Base* p = new Derived;
p->stat();    // Base::stat     <- static binding: decided by the POINTER's type
p->dyn();     // Derived::dyn   <- dynamic binding: decided by the OBJECT's type
delete p;

Examples in one line each: function/operator overloading is static binding (the compiler picks the overload from the argument types); a virtual function overridden in a derived class is dynamic binding. MCQ traps: dynamic binding needs both virtual and access through a pointer or reference — calling a virtual function on an object by value binds statically and causes object slicing; and virtual is inherited, so an override is virtual even if you omit the keyword.

Q6. Why can't an abstract class be instantiated directly?

Ans: Because it is deliberately incomplete. A class becomes abstract the moment it declares at least one pure virtual function (virtual void draw() = 0;). That declaration puts a slot in the class's vtable with no valid function address behind it, so if the compiler allowed you to create the object, a call to draw() on it would jump into nothing. The C++ standard therefore makes Shape s; a compile-time error — the mistake is caught by the compiler, not at run time.

The purpose of an abstract class is to define a contract/common interface that every concrete descendant must fulfil. Even though you cannot create objects of it, you certainly can and do use pointers and references to it — that is exactly the mechanism of run-time polymorphism:

class Shape {
public:
    virtual double area() const = 0;       // pure virtual -> Shape is ABSTRACT
    virtual ~Shape() = default;            // virtual dtor: essential in a base
    void describe() const { cout << "Area = " << area() << '\n'; }  // may have real code too
};
class Circle : public Shape {
    double r;
public:
    explicit Circle(double r) : r(r) {}
    double area() const override { return 3.14159 * r * r; }
};

// Shape s;                 // ERROR: cannot declare variable of abstract type 'Shape'
Shape* p = new Circle(2);   // OK  -- pointer to abstract base is legal
p->describe();              // prints: Area = 12.5664   (late binding via vtable)
delete p;                   // safe only because ~Shape() is virtual

Key points examiners test: an abstract class may still have constructors, data members and fully implemented functions (subclasses use them); a derived class that fails to override every inherited pure virtual function stays abstract itself; and if you remove the = 0, the class instantly becomes concrete and instantiable.

Q7. Explain coupling and cohesion, and why "low coupling, high cohesion" is the design goal.

Ans: Coupling measures how strongly one class depends on the internals of another — it is a property between modules. Cohesion measures how closely related the responsibilities inside a single class are — a property within a module. The standard design goal is low coupling and high cohesion, and the two pull in the same direction: a class that does exactly one thing needs to know very little about other classes.

  • Low coupling means a change in class B rarely forces a change in class A. Classes can then be tested in isolation, reused in other programs, and replaced (e.g. swap a FileLogger for a NetworkLogger) without a ripple of edits. Coupling is reduced by depending on abstract interfaces rather than concrete classes, and by never exposing data members publicly.
  • High cohesion means a class has one clear reason to exist. A Student class holding name, roll number and GPA is cohesive; a Utility class that also prints payslips, parses XML and opens sockets is not — it must be edited for unrelated reasons and is impossible to name properly.
// HIGH coupling: Report knows the concrete type and its internals
class FileLogger { public: void writeToDisk(const string& s); };
class Report { FileLogger lg; public: void run() { lg.writeToDisk("done"); } };

// LOW coupling: Report depends only on an abstract interface
class ILogger { public: virtual void log(const string&) = 0; virtual ~ILogger() = default; };
class FileLogger2 : public ILogger { public: void log(const string& s) override; };
class Report2 {
    ILogger& lg;                                   // any logger will do
public:
    explicit Report2(ILogger& l) : lg(l) {}
    void run() { lg.log("done"); }                 // swap loggers, Report2 unchanged
};

Why it matters in exam terms: these are the two classic design-quality metrics of OOP. Encapsulation directly lowers coupling (clients cannot depend on hidden data), and the single-responsibility principle directly raises cohesion. High coupling with low cohesion produces "spaghetti objects" — code that is brittle, untestable and unreusable, which is precisely what OOP was introduced to avoid.

Q8. Differentiate between a constructor and a destructor.

Ans: A constructor is a special member function that runs automatically when an object is created, to bring it into a valid state; a destructor runs automatically when the object dies, to release whatever the object acquired. Together they implement RAII (Resource Acquisition Is Initialization) — the C++ idiom that ties a resource's lifetime to an object's lifetime so cleanup cannot be forgotten, even if an exception is thrown.

AspectConstructorDestructor
NameSame as the classSame as the class, prefixed with ~
Return typeNone (not even void)None (not even void)
ParametersMay take parametersNever takes parameters
OverloadingCan be overloaded (default, parameterised, copy, move)Exactly one per class; cannot be overloaded
Can be virtual?NO — the vtable is not set up yetYES — and SHOULD be in any polymorphic base class
Order in a hierarchyBase first, then derivedDerived first, then base (reverse)
PurposeAllocate/initialise resourcesRelease resources
class Buffer {
    int* data; int n;
public:
    Buffer(int n) : data(new int[n]), n(n) { cout << "ctor\n"; }  // acquires
    ~Buffer() { delete[] data; cout << "dtor\n"; }                // releases
};
{
    Buffer b(10);   // prints "ctor"
}                   // prints "dtor" automatically at end of scope -- no leak

MCQ traps to remember: a constructor can never be virtual (the vptr is only installed by the constructor, so there is nothing to dispatch through), but a destructor in a polymorphic base class should always be virtual — otherwise delete basePtr; on a derived object invokes only the base destructor and leaks the derived part (undefined behaviour). Also note that constructors run base → derived while destructors run derived → base.

📌 Sample & Repeated FPSC Questions (2016–2026)

2021, Paper I, Q4(a) (8 marks) — Explain object oriented programming paradigm. Write a detailed note on any two of the principles of object oriented programming paradigm.

OOP is a paradigm that structures software as interacting objects — instances of classes bundling data (state) and behavior (methods) — rather than a sequence of procedures acting on separate data, as in procedural programming. It rests on four pillars: Encapsulation, Inheritance, Polymorphism, Abstraction.

  • Encapsulation: bundles an object's data with the methods operating on it, restricting direct external access via private/protected, exposing only a controlled public interface. Protects data integrity and hides implementation so it can change later without breaking client code.
  • Inheritance: lets a derived class acquire the attributes/behavior of a base class, establishing an "is-a" relationship and enabling reuse — e.g. class SavingsAccount : public Account reuses Account's fields while adding interest-specific logic, avoiding duplicated code.

Key reasoning points:

  • Examiners want any two principles explained with a concrete C++ example each — listing all four with no depth loses marks versus fewer, deeper explanations.
  • Always tie each principle back to why it helps (data integrity, code reuse), not just a bare definition.

Exam angle: "any two" questions are marks-efficient if you pick the pair you can illustrate with code fastest — Encapsulation + Inheritance is usually the safest combination.


2016, Paper I, Q4(c) (8 marks) — Suppose you are designing software for a Medical Store, using object oriented techniques. Decide and define classes (like 'Person', 'Customer', 'Salesman', 'Purchase' etc.) and their attributes in C++ or Java syntax. You are not required to write any member functions and main programme. You must use inheritance where suitable.

class Person {
protected:
    int id; char name[50]; char phone[15]; char address[100];
};
class Customer : public Person { char customerType[20]; };   // walk-in / regular
class Salesman  : public Person { float salary; char shift[10]; };
class Medicine  { char medicineCode[15]; char name[50]; float unitPrice; int stockQuantity; char expiryDate[12]; };
class Purchase  { Customer customer; Salesman servedBy; char purchaseDate[12]; float totalAmount; };

Key reasoning points:

  • Customer and Salesman both inherit shared personal-detail fields from Person — exactly why the question mandates "use inheritance where suitable."
  • Purchase composes a Customer and a Salesman (has-a), not inherits from them (is-a) — showing both relationship types applied correctly in one design.
  • This is a "decide the classes" question, not a coding question — a clean, minimal class list with the right is-a/has-a choices earns most of the marks even without full member functions or a main().

📜 Past-Paper Questions & MCQs (CSS 2016–2026)

2019 · Paper-I · Q4(a) · 10 marks

Q. How can the OOP paradigm be associated with real-world problems? Explain.

Ans.

  • Objects model real things. A student, bank account, car or library book becomes an object with data (attributes) and behaviour (methods).
  • Class = blueprint, object = instance. Account is the class, and Ali's account is an object.
  • Encapsulation hides internal data. The balance can be changed only through deposit() and withdraw(), just like an ATM protects the vault.
  • Inheritance reuses common features: SavingAccount and CurrentAccount extend Account, like Car and Van extend Vehicle.
  • Polymorphism lets one message behave differently (calculateProfit() differs for each account type).
  • Abstraction shows only essentials (a driver uses the steering wheel without knowing the engine).
  • Benefits: modular design, reuse, easier maintenance, and teamwork.
  • Examples: banking, hospital, library and e-commerce systems.

2019 · Paper-I · Q4(b) · 10 marks

Q. Discuss critical reasons given by professionals for not supporting the OOP paradigm.

Ans.

  • Steep learning curve: classes, inheritance and design patterns are harder than plain procedures.
  • Performance overhead: objects, virtual calls and dynamic allocation use more memory and time than simple procedural code.
  • Over-engineering: small problems get unnecessary class hierarchies.
  • Fragile base class and tight coupling: a change in a parent class can break all derived classes, and deep inheritance is hard to understand.
  • Hidden state: many mutable objects make concurrent and parallel programs error-prone.
  • Poor fit for some domains: numerical, data-processing and functional-style tasks are often simpler with procedural or functional programming.
  • Reuse promise not always met: poorly designed classes are not truly reusable.
  • Testing and debugging: behaviour spread across many classes is harder to trace.
  • Conclusion: OOP is powerful for large, long-lived systems, but it should be used with good design and not everywhere.

2020 · Paper-I · Q4(b) · 4 marks

Q. How is object encapsulation useful? Explain.

Ans.

  • Encapsulation wraps data and the functions that use it into one unit (class) and hides the data with private access.
  • Data protection: outside code cannot corrupt the data. It must use public methods that can validate input.
  • Easier maintenance: the internal implementation can change without breaking users of the class.
  • Modularity and reuse: each class is a self-contained unit.
  • Example: balance is private in Account, and withdraw() rejects an amount larger than the balance.

2021 · Paper-I · Q4(a) · 8 marks

Q. Explain the object-oriented programming paradigm. Write a detailed note on any two of its principles.

Ans.

  • OOP paradigm: a way of designing software as a set of interacting objects that combine data and behaviour. It is built on classes and objects, encapsulation, abstraction, inheritance and polymorphism.
  • Encapsulation: data and methods are bundled in a class, and access is controlled with private, protected and public. This protects data and hides implementation.
  • Inheritance: a derived class acquires the members of a base class and can add or change behaviour. It promotes reuse, as in Student : public Person.
  • Other principles: abstraction (show essentials only) and polymorphism (one interface, many forms).

2023 · Paper-I · Q4(c) · 6 marks

Q. Differentiate between abstraction and encapsulation.

Ans.

BasisAbstractionEncapsulation
IdeaShowing only essential featuresBundling data and methods and hiding data
FocusDesign level: what an object doesImplementation level: how data is protected
Achieved byAbstract classes and interfacesAccess specifiers (private, protected, public)
ExampleA Shape class exposing area()A private balance with public getters/setters

MCQs

2024 P-I · Q6. Which was the first purely object-oriented programming language developed?

  • (A) Java
  • (B) C++
  • (C) SmallTalk ✅
  • (D) None of these
  • Answer: (C) SmallTalk.
  • Why: in Smalltalk (developed at Xerox PARC in the 1970s) everything, even numbers and classes, is an object. Java and C++ are hybrid languages that also have primitive types and procedural features.