Classes and Objects

Learning Objectives

Distinguish class, object, instance, state, identity and behaviour.

Write a class with data members, member functions, a static member and a constant.

Explain why two objects with equal values can still be different objects.

Model a simple domain object and name its invariants.

Prerequisites: Module 1.

Intuition and Definition

A class is a blueprint: it defines a type — the data each object holds and the operations allowed on it. An object (or instance) is a concrete thing built from that blueprint at run time. Every object has:

State — the current values of its data members (isOn == true).

Behaviour — the member functions you can call (turnOn()).

Identity — it is *this particular* object, distinct from every other one even if their state is equal.

Data member / field / attribute A variable stored inside each object Member function / method A function that operates on an object

Belongs to the class itself, shared by all objects A type declared inside a class (e.g. Lamp::Mode)

A value fixed for the class (static constexpr) A value calculated on request rather than stored

Why It Matters

Classes let you name concepts from the problem domain and give each one an explicit set of operations. A caller never needs to know *how* a lamp stores its state — only that it can be turned on and off.

Syntax and Language Notes

class Lamp {
public:
    enum class Mode { Normal, Dimmed };          // nested type
    static constexpr int MaxBrightness = 100;    // class constant
    void turnOn() { on = true; }
    bool isOn() const { return on; }             // const: does not modify the object
private:
    bool on = false;                             // default member initializer
};
// Java (not run)
public class Lamp {
    public static final int MAX_BRIGHTNESS = 100;
    private boolean on = false;
    public void turnOn() { on = true; }
    public boolean isOn() { return on; }
}
public class Lamp
{
    public const int MaxBrightness = 100;
    public bool IsOn { get; private set; }       // property with a private setter
    public void TurnOn() => IsOn = true;
}
class Lamp:
    MAX_BRIGHTNESS = 100          # class attribute
    def __init__(self):
        self.on = False           # instance attribute
    def turn_on(self):
        self.on = True
class Lamp {
  static readonly MAX_BRIGHTNESS = 100;
  private on = false;
  turnOn(): void { this.on = true; }
  isOn(): boolean { return this.on; }
}

Complete Example

#include <iostream>
#include <string>

class Lamp {
public:
    static constexpr int MaxBrightness = 100;

    explicit Lamp(std::string name) : name_(std::move(name)) { ++created_; }

    void turnOn()  { on_ = true; }
    void turnOff() { on_ = false; }
    void setBrightness(int b) {
        if (b < 0) b = 0;                      // keep the invariant 0..MaxBrightness
        if (b > MaxBrightness) b = MaxBrightness;
        brightness_ = b;
    }
    bool isOn() const { return on_; }
    int brightness() const { return on_ ? brightness_ : 0; }   // computed: 0 when off
    const std::string& name() const { return name_; }

    static int created() { return created_; }  // shared by all lamps

private:
    std::string name_;
    bool on_ = false;
    int brightness_ = 50;
    static inline int created_ = 0;
};

bool sameState(const Lamp& a, const Lamp& b) {
    return a.isOn() == b.isOn() && a.brightness() == b.brightness();
}

int main() {
    Lamp desk("desk");
    Lamp bed("bed");
    desk.turnOn();
    desk.setBrightness(250);                   // clamped to 100

    std::cout << desk.name() << ": on=" << desk.isOn() << " brightness=" << desk.brightness() << "\n";
    std::c
…
desk: on=1 brightness=100
bed: on=0 brightness=0
same state? 1
same object? 0
desk after alias.turnOff(): on=0
lamps created: 2

Step-by-Step Walkthrough

Lamp desk("desk") creates an object; the constructor stores the name and increments the static counter shared by all lamps.

setBrightness(250) clamps to 100 — the class protects its own rule.

brightness() is a computed property: a lamp that is off reports 0 even though brightness_ still holds 50.

After bed is turned on at 100, both lamps have equal state but are different objects (&desk == &bed is false).

alias is a reference — another name for desk. Turning it off changes desk, because there is only one object.

classDiagram
  class Lamp {
    +MaxBrightness : int = 100$
    -name_ : string
    -on_ : bool
    -brightness_ : int
    -created_ : int$
    +turnOn()
    +turnOff()
    +setBrightness(b)
    +brightness() int
    +created() int$
  }

Common Mistakes

Confusing equality with identity. Two objects with the same field values are equal, not identical; modifying one never changes the other unless they are the same object (alias, reference, pointer).

Forgetting const on read-only member functions. Then you cannot call them on a const Lamp&.

Public data members for everything. Callers can then put the object into states the class never intended (brightness 250).

Static data used as hidden global state. A static counter is fine; static *mutable configuration* makes code hard to test.

Trade-offs and Alternatives

A plain struct with public fields is perfectly fine for passive data with no rules (a 2D point, a DTO). Use a class when there is an invariant to protect.

Computed properties cost a calculation on each call but can never become stale; stored values are faster but must be kept in sync.

Real-World Use

Modelling exercise — for each class, state, behaviour and invariants:

Invariant / invalid state balance, owner, status deposit, withdraw, close balance ≥ 0 unless overdraft allowed; closed accounts accept no deposits last reading, calibration offset, fault flag read, calibrate reading within physical range (−40…150 °C) add, remove, total quantities > 0; no duplicate product lines speed, warnings, display mode show, raiseWarning, clear displayed speed never negative; critical warnings cannot be hidden

Practice

Q1. (Easy) What is printed by std::cout << Lamp::MaxBrightness; and why doesn't it need an object?

Answer: 100. It is a static constexpr class constant — it belongs to the class, not to any object.

Q2. (Medium) Write a Counter class with increment(), value() and a static totalIncrements() across all counters.

Q3. (Advanced) A Book class has public fields isbn, title, copiesAvailable. List two invalid states a caller could create and redesign the class to prevent them.

Answer: A caller could set copiesAvailable = -3 or replace the isbn with an invalid string. Make the fields private, validate the ISBN in the constructor, make it immutable after construction (no setter), and replace the counter setter with checkout() / giveBack() operations that refuse to go below zero.

#include <iostream>
class Counter {
    int value_ = 0;
    static inline int total_ = 0;
public:
    void increment() { ++value_; ++total_; }
    int value() const { return value_; }
    static int totalIncrements() { return total_; }
};
int main() {
    Counter a, b;
    a.increment(); a.increment(); b.increment();
    std::cout << a.value() << " " << b.value() << " " << Counter::totalIncrements() << "\n";
}

Interview Question

Q1. "What is the difference between a class and an object, and between equality and identity?"

Answer: A class is a compile-time definition of a type (data + operations); an object is a run-time instance of it occupying memory. Equality compares *state* (two lamps with the same on/off and brightness); identity asks whether two references denote the *same object* (same address in C++, is in Python, == on references in Java, ReferenceEquals in C#). Equal objects may be distinct; identical references are always equal.

Quick Quiz

Q1. Where is a static data member stored — in each object or once for the class?

Answer: Once for the class; all objects share it.

Q2. What does const after a member function signature promise?

Answer: That the function does not modify the object's (non-mutable) data, so it can be called on const objects.

Q3. Two variables refer to the same object. You modify it through one. What does the other see?

Answer: The change — there is only one object.

Summary

Class = type definition; object = run-time instance with state, behaviour and identity.

Static members belong to the class; computed properties derive values on demand.

Equality (same state) ≠ identity (same object).

Related: Module 3 (encapsulation), Module 10 (static members), Module 12 (copying and identity).