OOP · Getting Started
Classes & Objects
Write the shape once. Create the data as often as you need. Blueprints, state, birth, identity and memory — the five ideas that turn a class definition into something you can actually reason about.
Sign in to track your score
What you'll learn
- Read a C++ class and say what is the blueprint and what are the objects.
- Decide whether a piece of data belongs to one object or to the class.
- Write a constructor so an object is never usable before it is valid.
- Explain what this refers to during any given call.
- Tell a copy apart from a reference, and predict what each one changes.
How to use this module. Wherever you see THINK, PREDICT or SPOT THE PROBLEM, stop and answer before reading on. The answer always follows immediately — cover it with your hand if you need to. Every code sample here compiles and was run to confirm its output.
Open a ride-hailing app at 8 a.m. in any large city. Right now, roughly 40,000 trips are running at the same time.
Every one of them is different: a different rider, a different driver, a different pickup point, a different fare, a different status.
And yet every one of them has exactly the same shape. A trip always has a rider, a driver, a fare and a status. You can always do the same things to it: start it, end it, cancel it.
So the system holds 40,000 things that are all different and all structurally identical.
Think — You have to represent those 40,000 trips in code.
What would you have to write 40,000 times, and what would you write only once?
The shape gets written once. The data gets created 40,000 times.
That single sentence is what this entire module is about — and the rest of it is just making that idea precise enough to use.
2.1 Class vs object: blueprint vs the real thing
An office building has three coffee machines. Same model, same buttons, same tank size.
But right now: the lobby machine has 482 g of beans and has served 3 cups today. The one on floor 3 is empty and has served none. The one in the café has 900 g and has served 41.
| Real World | In Code |
|---|---|
| The model — what every machine has and can do | the class |
| Each physical machine | an object |
| The beans and cup count inside one machine | that object's state |
The class is written once by the engineer who designed the model. The objects are the machines actually bolted to the floor.
class CoffeeMachine {
public:
std::string location = "Unassigned";
int beansGrams = 0;
int cupsServed = 0;
void brew() { beansGrams -= 18; cupsServed++; }
void refillBeans(int grams) { beansGrams += grams; }
};
int main() {
CoffeeMachine lobby;
lobby.location = "Lobby";
lobby.refillBeans(500);
lobby.brew();
CoffeeMachine floor3;
floor3.location = "Floor 3";
std::cout << lobby.cupsServed << " " << floor3.cupsServed;
}
What is happening: class CoffeeMachine {... }; describes the model. CoffeeMachine lobby; builds one actual machine from it.
Why it matters: the class costs no memory and holds no values. Only objects do.
Quick check — In the code above, which name is the class, and which names are objects?
Answer — CoffeeMachine is the class. lobby and floor3 are two objects created from it.
Predict —
lobby.brew()was called once.floor3was never touched.What does the program print?
- A.
1 1- B.
1 0- C.
0 0- D.
3 41
Answer — B, it prints 1 0 Each object owns its own cupsServed. Brewing in the lobby cannot reach into the machine on floor 3. Changing one object's state never changes another's.

One class defines the shape. Each object carries its own values.
2.2 Fields & methods: state, behaviour, instance vs static
A parking garage prints a ticket when a car enters.
| Real World | In Code | Name |
|---|---|---|
| plate, entry time | data the ticket holds | fields (state) |
| calculate the fee, close the ticket | things the ticket can do | methods (behaviour) |
So: fields are what an object knows, methods are what it does. Together they form the object.
Two kinds of data
Now a sharper question.
- The plate on a ticket belongs to that one ticket.
- The hourly rate is the same for every ticket in the garage.
- The number of tickets issued today is a single number for the whole garage.
Storing the hourly rate separately inside every ticket would mean 400 copies of the same value — and 400 places to update when the price changes.
That is exactly what static is for.
- Instance field — every object gets its own copy.
- Static field — one copy, owned by the class itself, shared by every object.
class ParkingTicket {
public:
static inline int hourlyRate = 40; // one value for the whole class
static inline int ticketsIssued = 0; // one shared counter
std::string plate; // each ticket has its own
int entryHour = 0;
int exitHour = 0;
void issue(std::string p, int hour) {
plate = p;
entryHour = hour;
ticketsIssued++;
}
int fee() const { return (exitHour - entryHour) * hourlyRate; }
};
Predict — Two tickets are issued, then the first one exits after 3 hours:
What does this print?
> ParkingTicket t1, t2;
> t1.issue("KA-01-4417", 9);
> t2.issue("MH-12-8890", 10);
> t1.exitHour = 12;
>
> std::cout << t1.fee() << " "
> << ParkingTicket::ticketsIssued << " "
> << t2.ticketsIssued;
>
Answer — it prints 120 2 2
t1.fee()is(12 − 9) × 40 = 120. The rate came from the shared static field.ticketsIssuedis 2 for both, because there is only one of it. Each call toissue()incremented the same counter.
Both spellings work, but prefer ParkingTicket::ticketsIssued — it says out loud that the value belongs to the class, not to any one ticket.

Instance data lives in each object. Static data lives with the class.
Watch out — A static field is not a global variable. A global belongs to the whole program and anyone can reach it. A static field belongs to one class, is named through that class, and follows that class's access rules. The scope is completely different.
Methods can be static too. A static method belongs to the class, so it has no current object to work on — which means it cannot touch plate or entryHour.
2.3 Constructors: how an object is born
A hotel booking that exists without a guest name, a room number or a number of nights is not a booking. It is a bug waiting to be found by a customer.
Think — An object comes into existence the moment you declare it.
How do you make sure it already holds sensible values at that exact moment?
Look at what happens without help:
class HotelBooking {
public:
std::string guestName;
int roomNumber; // no starting value
int nights; // no starting value
};
HotelBooking b; // which room? how many nights?
guestName is an empty string, which is at least harmless. But roomNumber and nights hold whatever happened to be in that memory — reading them is undefined behaviour. The object exists, and it means nothing.
A constructor is a special member function that runs automatically when an object is created, and its job is to leave that object in a valid state.
class HotelBooking {
public:
std::string guestName;
int roomNumber;
int nights;
std::string status;
HotelBooking(std::string guest, int room, int n)
: guestName(guest), roomNumber(room), nights(n), status("CONFIRMED") {}
};
int main() {
HotelBooking b("Lena Ortiz", 402, 3);
std::cout << b.guestName << " | room " << b.roomNumber
<< " | " << b.nights << " nights | " << b.status;
}
What is happening:
- Same name as the class, no return type — it does not return anything, it prepares something.
- The part after the colon gives each field its starting value before the body runs.
statusis set without anyone passing it in. Defaults can be decided by the constructor itself.
Why it matters: HotelBooking b; no longer compiles. Once you declare a constructor that takes arguments, the compiler stops generating the empty one — so a meaningless booking becomes impossible to create by accident.
The birth of an object, in four steps:
- Memory is set aside for the object.
- The constructor runs.
- Fields receive their starting values.
- The object is ready to use.
Predict — Immediately after
HotelBooking b("Lena Ortiz", 402, 3);— what doesb.statushold?
Answer — **** "CONFIRMED" The output is Lena Ortiz | room 402 | 3 nights | CONFIRMED. There is no moment where the object exists but is not yet filled in; step 4 cannot happen before step 3.

An object should never be usable before it is valid.
2.4 The this reference
Here is a music app. The method looks completely reasonable.
class Playlist {
public:
std::string name = "Untitled";
void setName(std::string name) {
name = name;
}
};
Spot the problem —
playlist.setName("Focus")runs without any error, and afterwards the playlist is still called"Untitled".Why?
Answer — There are two things called name: the field, and the parameter. Inside the method the parameter is closer, so it wins. The line name = name; assigns the parameter to itself and the field is never touched.
You need a way to say "the name belonging to the object I was called on." That is this:
void setName(std::string name) {
this->name = name; // field = parameter
}
this refers to the current object — the specific object the method was called on. Not the class, and not any other object.
Playlist morning, workout;
morning.setName("Morning Mix");
workout.setName("Workout");
std::cout << morning.name << " | " << workout.name; // Morning Mix | Workout
Predict —
setNameexists only once in memory, shared by both objects.During
workout.setName("Workout"), what doesthisrefer to?
Answer — the workout object The method is shared; this is not. Each call carries its own this, set to whichever object appears before the dot. That is how one copy of the code can update the right object every time.

One method, two objects, and this pointing wherever the call came from.
2.5 Objects in memory: references vs values
A delivery system tracks packages. Two variables can mean two different things here, and mixing them up causes some of the most confusing bugs a beginner meets.
class Package {
public:
std::string trackingId = "PK-0000";
std::string status = "IN_TRANSIT";
};
Package p1;
p1.trackingId = "PK-9921";
Package p2 = p1; // a copy
Package& ref = p1; // a reference
Those two lines look almost identical. They do completely different things.
Package p2 = p1;creates a second object and copiesp1's values into it. Two packages now exist.Package& ref = p1;creates no new object.refis another name for the package that already exists.
The & in the type is the whole difference.

A copy makes a second object. A reference is a second name for the same one.
Predict — Continuing from the code above:
What does
p1.statusbecome after the change throughref? And after the change throughp2?
> ref.status = "DELIVERED";
> p2.status = "LOST";
>
> std::cout << p1.status << " | " << p2.status << " | " << ref.status;
>
Answer — it prints DELIVERED | LOST | DELIVERED
- Writing through
refchangedp1, becauserefisp1. Same object, two names. - Writing through
p2leftp1alone, becausep2is a separate object that merely started with the same values.
Watch out — A C++ reference is not a pointer. A pointer is its own variable that stores an address and can be reassigned or left null. A reference is an alias: it must be bound to an object when it is created, it can never be re-bound to a different object, and it cannot be null. You use it exactly as if it were the original.
Why this matters in practice: passing a large object by value copies all of it; by reference it does not. That is why you see void ship(Package& p) far more often than void ship(Package p).
Common Misconceptions
❌ "A class and an object are the same thing."
✅ A class is the design and costs no memory. An object is a real instance with its own values.
💡 We use one word for both in speech: "the CoffeeMachine has a bean count" vs "this machine has 482 g."
❌ "Every object gets its own copy of a static field."
✅ There is exactly one copy, owned by the class. Every object sees the same value.
💡 Static fields are usually written inside the class body, so they look like they sit inside each object.
❌ "Static means global."
✅ A static field belongs to one class and is reached through that class's name and access rules. A global belongs to the entire program.
💡 Both survive for the whole run of the program, so people assume the scope is the same too. It is not.
❌ "A constructor is just a normal method you have to call."
✅ It runs automatically when the object is created. You never call it by name afterwards.
💡 It has a name and parentheses and a body, so it looks like any other method.
❌ "A constructor returns the object."
✅ It has no return type and returns nothing. It initializes an object that already exists in memory.
💡 HotelBooking b("Lena", 402, 3); reads like a function call that hands something back.
❌ " this refers to the class."
✅ this refers to the one specific object the method was called on — it differs from call to call.
💡 The method is written inside the class, so this feels like it should mean the class.
❌ "A reference creates a new object."
✅ It creates a new name for an object that already exists. No second object is made.
💡 It looks like a variable declaration, and every other variable declaration does create something.
❌ "Copying and referencing are basically the same, just different syntax."
✅ A copy gives you two independent objects. A reference gives you one object with two names. Changing one has opposite consequences.
💡 Package p2 = p1; and Package& ref = p1; differ by a single character.
Interview Corner
Q1 (Easy). What is the difference between a class and an object? A class is a blueprint describing what fields and methods a type has; it occupies no memory itself. An object is an instance built from it, with its own values. One class can produce any number of objects, each with independent state.
Q2 (Easy). What is the difference between a field and a method? A field is data the object holds — its state. A method is an operation the object can perform — its behaviour. The pairing of the two is what makes an object more than a struct full of variables.
Q3 (Medium). What is the difference between an instance field and a static field? An instance field gets a separate copy in every object. A static field exists once and is shared by all of them. Use static when the value describes the class as a whole — a shared rate, a counter, a limit.
Q4 (Medium). What does this print, and why?
class Elevator {
public:
static inline int buildingFloors = 12;
int currentFloor = 0;
void goTo(int f) { currentFloor = f; }
};
Elevator a, b;
a.goTo(7);
Elevator::buildingFloors = 15;
std::cout << a.currentFloor << " " << b.currentFloor << " " << b.buildingFloors;
It prints 7 0 15. currentFloor is an instance field, so moving a leaves b at 0. buildingFloors is static, so changing it through the class name is immediately visible through b.
Q5 (Medium). Why does a constructor have no return type? Because it does not produce a value — it initializes an object that the language has already created in memory. Its purpose is to guarantee that the object is in a valid state before anyone can use it.
Q6 (Conceptual). Why can a static method not use this ? this refers to the object a method was called on, and a static method is not called on any object. With no current object, this has nothing to refer to, so a static method can only touch static members.
Q7 (Conceptual). What does this print, and what is the bug?
class Playlist {
public:
std::string name = "Untitled";
void rename(std::string name) { name = name; }
};
Playlist p;
p.rename("Focus");
std::cout << p.name;
It prints Untitled. The parameter shadows the field, so the assignment writes the parameter to itself and the field is never touched. The fix is this->name = name;, which says explicitly that the left side belongs to the current object.
Q8 (Tricky). Is a C++ reference just another name for a pointer? No. A pointer is its own object holding an address — it can be null, reassigned, and needs dereferencing. A reference is an alias: bound at creation, never re-bound, never null, and used with the original's syntax. Overlapping problems, very different guarantees.
- 1.
class Elevator { public: static inline int buildingFloors = 12; int currentFloor = 0; void goTo(int f) { currentFloor = f; } }; int main() { Elevator a, b; a.goTo(7); Elevator::buildingFloors = 15; std::cout << a.currentFloor << " " << b.currentFloor << " " << b.buildingFloors; }What does this print?
- 2.
class Subscription { public: std::string plan; int months; Subscription(std::string p, int m) : plan(p), months(m) {} }; int main() { Subscription a("Pro", 6); Subscription b; // <-- this line std::cout << a.plan; }What happens?
- 3.
class Playlist { public: std::string name = "Untitled"; void rename(std::string name) { name = name; } }; int main() { Playlist p; p.rename("Focus"); std::cout << p.name; }What does this print?
- 4.
class Ticket { public: std::string seat = "A1"; }; int main() { Ticket t1; Ticket t2 = t1; Ticket& t3 = t1; t2.seat = "B2"; t3.seat = "C3"; std::cout << t1.seat << " " << t2.seat << " " << t3.seat; }What does this print?
- 5.
class MovieScreening { public: std::string movieTitle; int screenNumber; int seatsSold; double taxRate; // 8%, identical for every screening in the cinema };Which field is the best candidate to become
static?
The problem
Design a vending machine class for a company that operates several machines across a building.
Each machine is in a different location, holds a different amount of stock, and has collected a different amount of cash. But the company also wants to know how many machines it has deployed in total.
Work through it in seven steps:
- Identify the class.
- Identify the instance fields.
- Identify the methods.
- Identify what could reasonably be static.
- Write a constructor.
- Create two objects.
- Create a reference to one of them and act through it.
Hint — For every piece of data, ask one question: does this describe one machine, or the whole fleet?
"How many cans are left" describes one machine. "How many machines exist" describes the fleet. That question separates instance from static every single time.
Possible solution
class VendingMachine {
public:
static inline int machinesDeployed = 0; // belongs to the fleet
std::string location; // belongs to one machine
int cashCollected;
int itemsLeft;
VendingMachine(std::string location, int stock) {
this->location = location; // parameter shadows the field
cashCollected = 0;
itemsLeft = stock;
machinesDeployed++;
}
bool dispense(int price) {
if (itemsLeft == 0) return false;
itemsLeft--;
cashCollected += price;
return true;
}
};
int main() {
VendingMachine lobby("Lobby", 20);
VendingMachine gym("Gym", 5);
VendingMachine& nearest = lobby; // an alias, not a copy
nearest.dispense(120);
std::cout << lobby.itemsLeft << " " << gym.itemsLeft << " "
<< VendingMachine::machinesDeployed; // 19 5 2
}
Why this works
| Decision | Reason |
|---|---|
location, cashCollected, itemsLeft are instance fields | each machine has its own, and they change independently |
machinesDeployed is static | it counts machines; no single machine owns that number |
| The counter is incremented in the constructor | every machine is counted exactly once, at the only moment one can exist |
this->location | the parameter has the same name as the field, so this disambiguates |
nearest is a reference | dispensing through it changes lobby itself, which is the point — a copy would have silently thrown the sale away |
Two questions to check yourself:
- If line 7 were
VendingMachine nearest = lobby;instead, what wouldlobby.itemsLeftprint? 20 — the sale would have landed on a throwaway copy. - Could
dispense()be static? No. It changesitemsLeft, which belongs to a particular machine, and a static method has no current object to work on.