Lesson 9 • Intermediate
Object-Oriented Programming
By the end of this lesson you'll be able to design your own C++ classes — bundling data with the functions that act on it, protecting that data behind a clean interface, and bringing objects to life with constructors. This is the paradigm that organises every large C++ program you'll ever read.
What You'll Learn
- Define a class and create objects from it
- Add data members and member functions (methods)
- Control access with public, private, and protected
- Write default, parameterized, and initializer-list constructors
- Use destructors and the this pointer correctly
- Apply encapsulation with getters/setters, and know struct vs class
const and the & reference symbol here; everything else we build from scratch.💡 Real-World Analogy
A class is a cookie cutter; an object is a cookie you stamp out with it. The cutter defines the shape (what data and behaviour every cookie has), but each cookie is its own thing with its own decorations (its own data). You can make a hundred cookies from one cutter, and changing the icing on one doesn't touch the others. The private part of a class is like the secret recipe sealed inside — people can eat the cookie (use its methods) without ever seeing, or messing up, the recipe.
1. Classes, Objects & Members
A class bundles together data members (the variables each object stores) and member functions, also called methods (the functions that act on that data). An object is one concrete instance of the class. You reach into an object with the . (dot) operator: myDog.bark(). Read this worked example, run it, and notice that each object keeps its own copy of the data.
Worked example: a Dog class
Read every comment, run it, and check the output matches.
#include <iostream>
#include <string>
using namespace std;
// A class is a BLUEPRINT. It bundles DATA (members) with the
// FUNCTIONS that work on that data (member functions / methods).
class Dog {
public: // anyone can touch what's below this label
// --- data members (the state each object carries) ---
string name;
int ageYears;
// --- member function (a method = a function that lives on the class) ---
void bark() {
cout << name << " says: Wo
...Your turn. The Book class below is almost complete — fill in the two blanks marked ___ using the hints, then run it.
🎯 Your turn: finish the Book class
Fill in the ___ blanks, then check your output against the expected line.
#include <iostream>
#include <string>
using namespace std;
class Book {
public:
// 🎯 YOUR TURN — fill in the blanks marked with ___
// 1) Two data members: a string "title" and an int "pages"
string title;
int ___; // 👉 name this member pages
// 2) A member function that prints the book's details
void show() {
cout << title << " (" << ___ << " pages)" << endl; // 👉 print the pages member
}
};
int main() {
Book b;
b.title = "C++
...2. Access Specifiers & Encapsulation
Encapsulation means hiding an object's data and only letting the outside world touch it through safe, controlled methods. You enforce it with access specifiers: private: (only the class itself can see it), public: (anyone with an object can use it), and protected: (the class and any classes that inherit from it — you'll meet inheritance next lesson). The pattern is: keep data private, expose public methods. A method that only reads data is a getter; one that changes it (with validation) is a setter.
Worked example: a BankAccount that protects its balance
See how private data plus public methods keep the object valid.
#include <iostream>
#include <string>
using namespace std;
class BankAccount {
private: // hidden from the outside world
string owner;
double balance; // NOBODY can set this directly -> it stays valid
public: // the safe, controlled "front door"
// Constructor: runs automatically when an object is created.
// The : owner(name), balance(...) part is the INITIALIZER LIST.
BankAccount(string name, double opening)
: owne
...Now you try. Complete the constructor's initializer list and add the missing const to the getter:
🎯 Your turn: finish the Thermostat class
Complete the initializer list and mark the getter const, then run it.
#include <iostream>
#include <string>
using namespace std;
class Thermostat {
private:
double tempC; // private -> protected from bad values
public:
// 🎯 YOUR TURN — complete the constructor and the getter.
// 1) Constructor: store the starting temperature in tempC
// using an initializer list ( : member(value) )
Thermostat(double start) : ___ {} // 👉 write tempC(start)
// 2) A read-only getter that returns tempC
double getTemp() ___ { return te
...3. Constructors, this & Destructors
A constructor is a special method that runs automatically when an object is created — its job is to put the object in a valid starting state. A class can have several: a default constructor (no arguments) and one or more parameterized constructors. The cleanest way to set members is the initializer list — the : member(value) part written before the { body. Inside a method, this is a pointer to the current object, handy when a parameter shares a member's name (this->name = name;). The destructor (named ~ClassName) runs automatically when the object is destroyed — the place to release any resources it held.
Worked example: default vs parameterized constructors
Watch the constructors and destructors fire automatically.
#include <iostream>
#include <string>
using namespace std;
class Player {
private:
string name;
int score;
public:
// Default constructor — no arguments. Used when you write Player p;
Player() : name("Guest"), score(0) {
cout << "Default Player created: " << name << endl;
}
// Parameterized constructor — takes arguments.
// 'this' is a pointer to THIS object; this->name means "my own name".
Player(string name, int score) {
this->name = name;
...🔎 Deep Dive: struct vs class
In C++ a struct and a class are almost identical — both can have data, methods, and constructors. The only real difference is the default access level:
structmembers are public by default.classmembers are private by default.
By convention, reach for struct when you just need a plain bundle of data with no hidden rules (like a 2D Point), and class when you want encapsulation. Picking the right one signals your intent to the next reader.
struct Point { double x, y; }; // x and y are public
class Wallet { double cash; }; // cash is private (hidden)Putting It Together: an Inventory
This program uses everything from the lesson at once — a plain struct Point, an encapsulated Inventory class with a constructor, a private vector, and public methods that control how it changes. Read it line by line; you understand every part now.
Worked example: struct + class together
Add or remove items and watch the controlled output update.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// A struct: like a class but members are PUBLIC by default.
// Great for plain bundles of data with no hidden rules.
struct Point {
double x;
double y;
};
// A class: members are PRIVATE by default -> use it when you want
// to protect data behind methods (encapsulation).
class Inventory {
private:
string shopName;
vector<string> items;
public:
Inventory(string name) : shopName(name) {}
void
...Pro Tips
- 💡 Prefer initializer lists:
Car(string b) : brand(b) {}initialises members directly and is more efficient than assigning inside the body — and it's required forconstmembers and references. - 💡 Mark read-only methods
const: a getter likedouble getBalance() constpromises not to change the object, so it works onconstobjects too. - 💡 Keep data private: expose behaviour, not raw fields. It lets you add validation later without breaking callers.
- 💡 One class, one job: a
Carshouldn't also print receipts. Small, focused classes are easier to reuse and test.
Common Errors (and the fix)
- Forgetting to initialize members: a member you never set holds a garbage value (e.g. an
intmight print-858993460). Always set every member in a constructor — ideally via an initializer list. - Public data breaks encapsulation: making
balancepublic lets any code writeacc.balance = -999;and corrupt the object. Keep itprivateand change it only through a validating method. - Missing constructor: writing
Player p("Aria", 100);when the class has no matching constructor gives "no matching function for call to Player::Player(...)". Add a constructor with those parameters. - Object slicing (a preview): assigning a derived object to a base-class value —
Animal a = myDog;— copies only the base part and "slices off" the rest. To keep full behaviour, use a reference or pointer (Animal& a = myDog;). You'll cover this fully in Inheritance. - "member is private" error: "'balance' is a private member of 'BankAccount'" means you touched a private field from outside. Go through a public getter/setter instead.
📋 Quick Reference
| Concept | Code | Meaning |
|---|---|---|
| Define a class | class Dog { ... }; | Blueprint (note the ;) |
| Create an object | Dog d; | One instance |
| Access a member | d.name = "Rex"; | Dot operator |
| Constructor | Dog(string n) : name(n) {} | Runs on creation |
| Destructor | ~Dog() { ... } | Runs on destruction |
| Read-only getter | int get() const | Won't modify object |
| The object itself | this->name | Pointer to current object |
Frequently Asked Questions
Q: What is the difference between a class and an object in C++?
A class is the blueprint — it defines what data members and member functions a thing has. An object is one concrete instance built from that blueprint. One Dog class can produce many Dog objects (Rex, Bella), each with its own independent data.
Q: What is the difference between struct and class in C++?
Technically only the default access level: members of a struct are public by default, while members of a class are private by default. By convention, use struct for plain data bundles with no hidden rules, and class when you want encapsulation — protecting data behind methods.
Q: Why should I make data members private?
Private data is the core of encapsulation. By hiding the data and only exposing controlled getters and setters, you guarantee an object can never hold an invalid state — a BankAccount balance can't be set to a nonsense value from outside, because the only way in is a deposit() method that validates first.
Q: What does the colon in BankAccount(string n) : owner(n) mean?
That is a member initializer list. It initialises each data member directly as the object is built, before the constructor body runs. It is more efficient than assigning inside the body and is required for const members and references.
Q: What is 'this' in C++?
this is a pointer to the current object — the specific instance a method was called on. You use this->name when a parameter has the same name as a data member, to say 'set MY name to the parameter'. You can write the member name on its own when there is no clash.
Q: When does the destructor run?
Automatically, when an object is destroyed — for a local object that is when it goes out of scope (the end of the function). Destructors run in the reverse order objects were created and are where you release resources like memory or file handles.
Mini-Challenge: Rectangle Class
No blanks this time — just a brief and an outline to keep you on track. Build the class yourself, run it, and check your output against the example in the comments. This is exactly the kind of small, self-contained class real programs are built from.
🎯 Mini-Challenge: build a Rectangle class
Write the members, constructor, and the area() and perimeter() methods.
#include <iostream>
#include <string>
using namespace std;
class Rectangle {
// 🎯 MINI-CHALLENGE: a Rectangle class
// 1. Private data members: double width and double height.
// 2. A constructor that takes width and height (use an initializer list).
// 3. A method double area() const that returns width * height.
// 4. A method double perimeter() const that returns 2 * (width + height).
//
// ✅ Expected output (for 4 x 3):
// Area: 12
// Perimeter:
...🎉 Lesson Complete
- ✅ A class is a blueprint; an object is one instance of it
- ✅ Classes bundle data members with member functions (methods)
- ✅
private/public/protectedcontrol who can touch each member - ✅ Constructors set up an object; initializer lists are the clean way to do it
- ✅ The destructor
~Class()cleans up;thispoints to the current object - ✅ Encapsulation = private data + public getters/setters that validate
- ✅
structdefaults to public,classdefaults to private - ✅ Next lesson: Inheritance & Polymorphism — reuse and extend your classes
Sign up for free to track which lessons you've completed and get learning reminders.