Classes - Introduction to OOP
What is a Class?
A class is like a blueprint for creating objects. Think of it like a cookie cutter - the class is the cutter, and the cookies you make are the objects!
Classes are similar to structures, but with superpowers:
- They can have functions (called methods)
- They can hide data (encapsulation)
- They can have special setup and cleanup functions
Object-Oriented Programming (OOP) is a way of thinking about programs as collections of objects that work together.
Your First Class
#include <iostream>
#include <string>
class Dog {
public:
std::string name;
int age;
void bark() {
std::cout << name << " says: Woof! Woof!" << std::endl;
}
};
int main() {
Dog myDog;
myDog.name = "Buddy";
myDog.age = 3;
std::cout << "My dog's name is " << myDog.name << std::endl;
std::cout << "He is " << myDog.age << " years old" << std::endl;
myDog.bark();
return 0;
}
Output:
My dog's name is Buddy
He is 3 years old
Buddy says: Woof! Woof!
Key differences from structs:
classkeyword instead ofstructpublic:makes members accessible (we'll explain this soon)- Methods (functions inside the class)
Class vs Struct
The main difference: classes have private by default, structs have public by default.
With struct:
struct Dog {
std::string name;
};
Everything is accessible automatically.
With class:
class Dog {
public:
std::string name;
};
You need public: to make things accessible.
When to use which:
- struct: Simple data containers with no behavior
- class: When you need methods and want to control access
Methods (Member Functions)
Methods are functions that belong to a class:
#include <iostream>
#include <string>
class Calculator {
public:
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int multiply(int a, int b) {
return a * b;
}
double divide(double a, double b) {
if (b == 0) {
std::cout << "Error: Division by zero!" << std::endl;
return 0;
}
return a / b;
}
};
int main() {
Calculator calc;
std::cout << "10 + 5 = " << calc.add(10, 5) << std::endl;
std::cout << "10 - 5 = " << calc.subtract(10, 5) << std::endl;
std::cout << "10 * 5 = " << calc.multiply(10, 5) << std::endl;
std::cout << "10 / 5 = " << calc.divide(10, 5) << std::endl;
return 0;
}
Output:
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
10 / 5 = 2
Constructors - Setup Functions
A constructor is a special method that runs automatically when you create an object:
#include <iostream>
#include <string>
class Student {
public:
std::string name;
int age;
double grade;
Student(std::string n, int a, double g) {
name = n;
age = a;
grade = g;
std::cout << "Created student: " << name << std::endl;
}
void display() {
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
std::cout << "Grade: " << grade << std::endl;
}
};
int main() {
Student student1("Alex", 13, 92.5);
Student student2("Sam", 14, 88.0);
std::cout << "\nStudent 1:" << std::endl;
student1.display();
std::cout << "\nStudent 2:" << std::endl;
student2.display();
return 0;
}
Output:
Created student: Alex
Created student: Sam
Student 1:
Name: Alex
Age: 13
Grade: 92.5
Student 2:
Name: Sam
Age: 14
Grade: 88
Constructor rules:
- Same name as the class
- No return type (not even void!)
- Called automatically when object is created
Default Constructor
If you don't provide values:
#include <iostream>
#include <string>
class Car {
public:
std::string brand;
int year;
Car() {
brand = "Unknown";
year = 0;
std::cout << "Default car created" << std::endl;
}
Car(std::string b, int y) {
brand = b;
year = y;
std::cout << "Car created: " << brand << std::endl;
}
void display() {
std::cout << year << " " << brand << std::endl;
}
};
int main() {
Car car1;
Car car2("Toyota", 2020);
car1.display();
car2.display();
return 0;
}
Output:
Default car created
Car created: Toyota
Unknown
2020 Toyota
Encapsulation - Hiding Data
Encapsulation means hiding data and only allowing access through methods:
#include <iostream>
#include <string>
class BankAccount {
private:
std::string accountNumber;
double balance;
public:
BankAccount(std::string accNum, double initialBalance) {
accountNumber = accNum;
balance = initialBalance;
}
void deposit(double amount) {
if (amount > 0) {
balance += amount;
std::cout << "Deposited: $" << amount << std::endl;
} else {
std::cout << "Invalid amount!" << std::endl;
}
}
void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
std::cout << "Withdrew: $" << amount << std::endl;
} else {
std::cout << "Invalid or insufficient funds!" << std::endl;
}
}
double getBalance() {
return balance;
}
void displayInfo() {
std::cout << "Account: " << accountNumber << std::endl;
std::cout << "Balance: $" << balance << std::endl;
}
};
int main() {
BankAccount account("123456", 1000.0);
account.displayInfo();
account.deposit(500);
account.withdraw(200);
account.displayInfo();
return 0;
}
Output:
Account: 123456
Balance: $1000
Deposited: $500
Withdrew: $200
Account: 123456
Balance: $1300
Why encapsulation?
- Protect data from invalid changes
- Control how data is accessed
- Can change implementation without breaking code
Getters and Setters
Getters retrieve data, setters modify data with validation:
#include <iostream>
#include <string>
class Person {
private:
std::string name;
int age;
public:
Person(std::string n, int a) {
name = n;
setAge(a);
}
std::string getName() {
return name;
}
void setName(std::string n) {
if (!n.empty()) {
name = n;
} else {
std::cout << "Name cannot be empty!" << std::endl;
}
}
int getAge() {
return age;
}
void setAge(int a) {
if (a >= 0 && a <= 150) {
age = a;
} else {
std::cout << "Invalid age!" << std::endl;
age = 0;
}
}
void display() {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
}
};
int main() {
Person person1("Alex", 13);
person1.display();
person1.setAge(14);
person1.display();
person1.setAge(200);
person1.display();
return 0;
}
Output:
Name: Alex, Age: 13
Name: Alex, Age: 14
Invalid age!
Name: Alex, Age: 14
this Pointer
this refers to the current object:
#include <iostream>
#include <string>
class Rectangle {
private:
double length;
double width;
public:
Rectangle(double length, double width) {
this->length = length;
this->width = width;
}
double getArea() {
return this->length * this->width;
}
double getPerimeter() {
return 2 * (this->length + this->width);
}
void display() {
std::cout << "Rectangle: " << length << " x " << width << std::endl;
std::cout << "Area: " << getArea() << std::endl;
std::cout << "Perimeter: " << getPerimeter() << std::endl;
}
};
int main() {
Rectangle rect(5.0, 3.0);
rect.display();
return 0;
}
Output:
Rectangle: 5 x 3
Area: 15
Perimeter: 16
When to use this:
- When parameter names match member names
- To be explicit about which variable you're using
- To return the current object
Class with Multiple Objects
#include <iostream>
#include <vector>
#include <string>
class Player {
private:
std::string name;
int score;
int level;
public:
Player(std::string n) {
name = n;
score = 0;
level = 1;
}
void addScore(int points) {
score += points;
if (score >= 100 * level) {
levelUp();
}
}
void levelUp() {
level++;
std::cout << name << " leveled up to " << level << "!" << std::endl;
}
void display() {
std::cout << name << " - Level " << level << ", Score: " << score << std::endl;
}
std::string getName() {
return name;
}
int getScore() {
return score;
}
};
int main() {
std::vector<Player> players;
players.push_back(Player("Alice"));
players.push_back(Player("Bob"));
players.push_back(Player("Charlie"));
std::cout << "=== Game Start ===" << std::endl;
players[0].addScore(50);
players[1].addScore(80);
players[2].addScore(120);
std::cout << "\n=== Current Standings ===" << std::endl;
for (Player& p : players) {
p.display();
}
return 0;
}
Output:
=== Game Start ===
Charlie leveled up to 2!
=== Current Standings ===
Alice - Level 1, Score: 50
Bob - Level 1, Score: 80
Charlie - Level 2, Score: 120
Real-World Example: Library System
#include <iostream>
#include <vector>
#include <string>
class Book {
private:
std::string title;
std::string author;
bool isCheckedOut;
public:
Book(std::string t, std::string a) {
title = t;
author = a;
isCheckedOut = false;
}
bool checkOut() {
if (!isCheckedOut) {
isCheckedOut = true;
std::cout << "Checked out: " << title << std::endl;
return true;
} else {
std::cout << title << " is already checked out!" << std::endl;
return false;
}
}
void returnBook() {
isCheckedOut = false;
std::cout << "Returned: " << title << std::endl;
}
void display() {
std::cout << title << " by " << author;
if (isCheckedOut) {
std::cout << " [Checked Out]";
} else {
std::cout << " [Available]";
}
std::cout << std::endl;
}
std::string getTitle() {
return title;
}
bool isAvailable() {
return !isCheckedOut;
}
};
class Library {
private:
std::vector<Book> books;
std::string name;
public:
Library(std::string n) {
name = n;
}
void addBook(std::string title, std::string author) {
books.push_back(Book(title, author));
std::cout << "Added: " << title << std::endl;
}
void displayBooks() {
std::cout << "\n=== " << name << " ===" << std::endl;
if (books.empty()) {
std::cout << "No books in library!" << std::endl;
return;
}
for (int i = 0; i < books.size(); i++) {
std::cout << (i + 1) << ". ";
books[i].display();
}
}
void checkOutBook(int index) {
if (index >= 0 && index < books.size()) {
books[index].checkOut();
} else {
std::cout << "Invalid book number!" << std::endl;
}
}
void returnBook(int index) {
if (index >= 0 && index < books.size()) {
books[index].returnBook();
} else {
std::cout << "Invalid book number!" << std::endl;
}
}
};
int main() {
Library myLibrary("City Library");
myLibrary.addBook("1984", "George Orwell");
myLibrary.addBook("To Kill a Mockingbird", "Harper Lee");
myLibrary.addBook("The Great Gatsby", "F. Scott Fitzgerald");
myLibrary.displayBooks();
std::cout << "\n=== Checking out books ===" << std::endl;
myLibrary.checkOutBook(0);
myLibrary.checkOutBook(1);
myLibrary.displayBooks();
std::cout << "\n=== Returning a book ===" << std::endl;
myLibrary.returnBook(0);
myLibrary.displayBooks();
return 0;
}
Output:
Added: 1984
Added: To Kill a Mockingbird
Added: The Great Gatsby
=== City Library ===
1. 1984 by George Orwell [Available]
2. To Kill a Mockingbird by Harper Lee [Available]
3. The Great Gatsby by F. Scott Fitzgerald [Available]
=== Checking out books ===
Checked out: 1984
Checked out: To Kill a Mockingbird
=== City Library ===
1. 1984 by George Orwell [Checked Out]
2. To Kill a Mockingbird by Harper Lee [Checked Out]
3. The Great Gatsby by F. Scott Fitzgerald [Available]
=== Returning a book ===
Returned: 1984
=== City Library ===
1. 1984 by George Orwell [Available]
2. To Kill a Mockingbird by Harper Lee [Checked Out]
3. The Great Gatsby by F. Scott Fitzgerald [Available]
Access Specifiers
Three levels of access:
public
Accessible from anywhere:
class Example {
public:
int publicVar;
};
private
Only accessible inside the class:
class Example {
private:
int privateVar;
};
protected
Accessible in class and derived classes (we'll learn this later):
class Example {
protected:
int protectedVar;
};
Const Methods
Methods that don't modify the object:
#include <iostream>
#include <string>
class Circle {
private:
double radius;
public:
Circle(double r) {
radius = r;
}
double getArea() const {
return 3.14159 * radius * radius;
}
double getCircumference() const {
return 2 * 3.14159 * radius;
}
void setRadius(double r) {
radius = r;
}
};
int main() {
Circle circle(5.0);
std::cout << "Area: " << circle.getArea() << std::endl;
std::cout << "Circumference: " << circle.getCircumference() << std::endl;
return 0;
}
The const after the method name means it won't change any member variables.
Common Mistakes
Mistake 1: Forgetting public:
class Dog {
std::string name;
};
Members are private by default! Add public::
class Dog {
public:
std::string name;
};
Mistake 2: Wrong Constructor Name
class Dog {
public:
void dog() {
}
};
Constructor must match class name exactly:
class Dog {
public:
Dog() {
}
};
Mistake 3: Trying to Access Private Members
class Dog {
private:
std::string name;
};
Dog myDog;
myDog.name = "Buddy";
Use a setter method instead!
Practice Exercises
1. Temperature Class Create a class that stores temperature and can convert between Celsius and Fahrenheit.
2. Todo List Class Create a class for managing a todo list with add, remove, and display methods.
3. Game Character Class Create a class for an RPG character with health, attack, defend, and level up methods.
Example solution for temperature:
#include <iostream>
class Temperature {
private:
double celsius;
public:
Temperature(double c) {
celsius = c;
}
double getCelsius() const {
return celsius;
}
double getFahrenheit() const {
return (celsius * 9.0 / 5.0) + 32.0;
}
void setCelsius(double c) {
celsius = c;
}
void setFahrenheit(double f) {
celsius = (f - 32.0) * 5.0 / 9.0;
}
void display() {
std::cout << celsius << "°C = " << getFahrenheit() << "°F" << std::endl;
}
};
int main() {
Temperature temp(25.0);
temp.display();
temp.setFahrenheit(98.6);
temp.display();
return 0;
}
What's Next?
You've learned the basics of Object-Oriented Programming! Next, we'll explore:
- Inheritance - Creating classes based on other classes
- Polymorphism - Objects that can take many forms
- Advanced OOP concepts
You're becoming a real software engineer! 🎯