Skip to main content

Chapter 4a - Classes & Objects

What Is Object-Oriented Programming?​

OOP organizes code around objects β€” bundles of related data (attributes) and behavior (methods). A class is the blueprint; an object is an instance of that blueprint.

Class: Dog          Object: myDog
───────────── ──────────────────
Attributes: name = "Rex"
name breed = "Labrador"
breed age = 3
age
Methods: myDog.bark() β†’ "Woof!"
bark() myDog.sit() β†’ "Rex sits."
sit()

Defining a Class​

class Dog {
public:
string name;
string breed;
int age;

void bark() {
cout << name << " says: Woof!" << endl;
}

void describe() {
cout << name << " is a " << age << "-year-old " << breed << "." << endl;
}
};
  • class keyword begins the definition.
  • public: means the members below are accessible from outside the class.
  • The definition ends with a semicolon after the closing brace β€” a common mistake to forget.

Creating Objects​

int main() {
Dog rex; // create an object
rex.name = "Rex";
rex.breed = "Labrador";
rex.age = 3;

rex.bark(); // Rex says: Woof!
rex.describe(); // Rex is a 3-year-old Labrador.

Dog buddy;
buddy.name = "Buddy";
buddy.breed = "Poodle";
buddy.age = 5;
buddy.describe();
}
πŸ§ͺ Try it
#include <iostream>
#include <string>
using namespace std;

class Car {
public:
string make;
string model;
int year;
double speed;

void accelerate(double amount) {
speed += amount;
cout << model << " accelerates to " << speed << " mph." << endl;
}

void brake(double amount) {
speed -= amount;
if (speed < 0) speed = 0;
cout << model << " slows to " << speed << " mph." << endl;
}
};

int main() {
Car myCar;
myCar.make = "Toyota";
myCar.model = "Supra";
myCar.year = 2024;
myCar.speed = 0;

myCar.accelerate(30);
myCar.accelerate(20);
myCar.brake(15);
return 0;
}

Access Modifiers​

ModifierAccessible from
publicAnywhere
privateOnly inside the class
protectedInside the class and subclasses (covered in Ch.4b)

Encapsulation β€” hiding internal data with private and exposing only what's needed through public methods β€” is one of OOP's core principles.

class BankAccount {
private:
double balance; // cannot be accessed directly from outside

public:
// Getter β€” read-only access
double getBalance() {
return balance;
}

// Setter β€” controlled write access
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}

void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
} else {
cout << "Invalid withdrawal." << endl;
}
}
};
Why Private?

If balance were public, any code could write account.balance = -9999. Making it private forces all changes through deposit and withdraw, where we can enforce rules.


Constructors​

A constructor is a special method that runs automatically when an object is created. It has the same name as the class and no return type.

class Dog {
public:
string name;
string breed;
int age;

// Constructor
Dog(string n, string b, int a) {
name = n;
breed = b;
age = a;
}

void describe() {
cout << name << " is a " << age << "-year-old " << breed << "." << endl;
}
};

int main() {
Dog rex("Rex", "Labrador", 3); // constructor called here
Dog buddy("Buddy", "Poodle", 5);

rex.describe();
buddy.describe();
}

Initializer List Syntax (Preferred)​

Dog(string n, string b, int a) : name(n), breed(b), age(a) {}

This initializes members directly instead of assigning inside the body β€” more efficient for complex types.

Default Constructor​

A constructor with no parameters (or all defaults):

Dog() : name("Unknown"), breed("Mixed"), age(0) {}

You can have multiple constructors (overloading):

Dog(string n) : name(n), breed("Unknown"), age(0) {}
Dog(string n, string b, int a) : name(n), breed(b), age(a) {}
πŸ§ͺ Full class with constructor
#include <iostream>
#include <string>
using namespace std;

class Rectangle {
private:
double width;
double height;

public:
Rectangle(double w, double h) : width(w), height(h) {}

double area() {
return width * height;
}

double perimeter() {
return 2 * (width + height);
}

void describe() {
cout << "Rectangle " << width << "x" << height
<< " | Area: " << area()
<< " | Perimeter: " << perimeter() << endl;
}
};

int main() {
Rectangle r1(5.0, 3.0);
Rectangle r2(10.0, 2.5);

r1.describe();
r2.describe();
return 0;
}

Destructors​

A destructor runs automatically when an object goes out of scope or is deleted. It cleans up resources.

class MyClass {
public:
MyClass() { cout << "Object created." << endl; }
~MyClass() { cout << "Object destroyed." << endl; }
};

int main() {
MyClass obj; // "Object created."
} // "Object destroyed." β€” automatic when main() ends

Destructors matter most when the class owns heap memory (covered in Ch.5a).


The this Pointer​

Inside any member function, this is a pointer to the current object. It's useful when a parameter has the same name as a member.

class Point {
public:
double x, y;

Point(double x, double y) {
this->x = x; // this->x is the member; x alone is the parameter
this->y = y;
}
};

Static Members​

A static member belongs to the class, not to any individual object. All instances share the same static variable.

class Counter {
public:
static int count; // declaration

Counter() { count++; }
~Counter() { count--; }

static int getCount() { return count; }
};

int Counter::count = 0; // definition (required outside the class)

int main() {
Counter a, b, c;
cout << Counter::getCount() << endl; // 3
}

Exercises​

Activity: Student Class

Design a Student class with private fields name, grade (0-100), and id. Add:

  • A constructor that sets all three fields.
  • A method letterGrade() that returns "A", "B", "C", "D", or "F".
  • A method describe() that prints all info.
πŸ“’ Solution
#include <iostream>
#include <string>
using namespace std;

class Student {
private:
string name;
int grade;
int id;

public:
Student(string n, int g, int i) : name(n), grade(g), id(i) {}

string letterGrade() {
if (grade >= 90) return "A";
if (grade >= 80) return "B";
if (grade >= 70) return "C";
if (grade >= 60) return "D";
return "F";
}

void describe() {
cout << "[ID " << id << "] " << name
<< " β€” " << grade << "/100 (" << letterGrade() << ")" << endl;
}
};

int main() {
Student s1("Alice", 93, 1001);
Student s2("Bob", 74, 1002);
Student s3("Carol", 58, 1003);

s1.describe();
s2.describe();
s3.describe();
return 0;
}
Activity: Stack Counter

Use the static member technique to write a class ObjectTracker that prints how many live instances exist at any point.

πŸ“’ Solution
#include <iostream>
#include <string>
using namespace std;

class ObjectTracker {
static int count;
string label;
public:
ObjectTracker(string l) : label(l) {
count++;
cout << label << " created. Total: " << count << endl;
}
~ObjectTracker() {
count--;
cout << label << " destroyed. Total: " << count << endl;
}
static int getCount() { return count; }
};

int ObjectTracker::count = 0;

int main() {
ObjectTracker a("A");
{
ObjectTracker b("B");
ObjectTracker c("C");
cout << "Inside block: " << ObjectTracker::getCount() << endl;
}
cout << "After block: " << ObjectTracker::getCount() << endl;
}