Imagine you're organizing a zoo. Instead of tracking "animal name 1", "animal age 1", "animal type 1", etc., you create an "Animal" blueprint that combines all related information and behaviors. That's OOP - organizing code around objects that represent real things!
A class is like an architect's blueprint - it defines what something should look like and what it can do. An object is the actual house built from that blueprint!
class Rectangle {
private:
double width;
double height;
public:
// Constructor - called when object is created
Rectangle(double w, double h) {
width = w;
height = h;
}
// Default constructor
Rectangle() {
width = 0;
height = 0;
}
// Methods (member functions)
double getArea() {
return width * height;
}
double getPerimeter() {
return 2 * (width + height);
}
// Setters - modify private data
void setWidth(double w) {
if (w > 0) {
width = w;
}
}
void setHeight(double h) {
if (h > 0) {
height = h;
}
}
// Getters - access private data
double getWidth() {
return width;
}
double getHeight() {
return height;
}
};
// Using the class
int main() {
Rectangle rect1(5, 3); // Create object with constructor
Rectangle rect2; // Create with default constructor
cout << "Area: " << rect1.getArea() << endl; // 15
cout << "Perimeter: " << rect1.getPerimeter() << endl; // 16
rect2.setWidth(10);
rect2.setHeight(4);
cout << "Rect2 area: " << rect2.getArea() << endl; // 40
return 0;
}
Encapsulation is like a car - you don't need to understand the engine to drive. You just use the steering wheel and pedals (public interface) while the complex mechanics (private data) are hidden!
Constructors are like a welcome party when an object is born, and destructors are the cleanup crew when it's time to go!
class Student {
private:
string name;
int* grades;
int numGrades;
public:
// Default constructor
Student() {
cout << "Default constructor called!" << endl;
name = "Unknown";
numGrades = 0;
grades = nullptr;
}
// Parameterized constructor
Student(string n, int num) {
cout << "Parameterized constructor for " << n << endl;
name = n;
numGrades = num;
grades = new int[numGrades]; // Dynamic allocation
// Initialize grades to 0
for (int i = 0; i < numGrades; i++) {
grades[i] = 0;
}
}
// Copy constructor (important for dynamic memory!)
Student(const Student& other) {
cout << "Copy constructor called!" << endl;
name = other.name;
numGrades = other.numGrades;
// Deep copy of array
grades = new int[numGrades];
for (int i = 0; i < numGrades; i++) {
grades[i] = other.grades[i];
}
}
// Destructor - cleanup!
~Student() {
cout << "Destructor for " << name << endl;
delete[] grades; // Free dynamic memory
}
void setGrade(int index, int grade) {
if (index >= 0 && index < numGrades) {
grades[index] = grade;
}
}
double getAverage() {
if (numGrades == 0) return 0;
int sum = 0;
for (int i = 0; i < numGrades; i++) {
sum += grades[i];
}
return static_cast(sum) / numGrades;
}
};
The 'this' pointer is like saying "myself" - it's how an object refers to itself!
Composition is like building with LEGO blocks - you create complex objects by combining simpler ones!
class Engine {
private:
int horsepower;
double displacement;
public:
Engine(int hp, double disp) : horsepower(hp), displacement(disp) {}
void start() {
cout << "Engine starting... Vroom!" << endl;
}
int getHorsepower() { return horsepower; }
};
class Wheel {
private:
double diameter;
string type;
public:
Wheel(double d, string t) : diameter(d), type(t) {}
void rotate() {
cout << "Wheel rotating..." << endl;
}
};
class Car {
private:
string make;
string model;
Engine engine; // Composition: Car "has an" Engine
Wheel wheels[4]; // Composition: Car "has" Wheels
public:
// Constructor initialization list
Car(string mk, string md, int hp)
: make(mk), model(md), engine(hp, 2.0) {
// Initialize all wheels
for (int i = 0; i < 4; i++) {
wheels[i] = Wheel(17.0, "All-Season");
}
}
void start() {
cout << "Starting " << make << " " << model << endl;
engine.start();
cout << "Ready to drive!" << endl;
}
void drive() {
cout << "Driving..." << endl;
for (int i = 0; i < 4; i++) {
wheels[i].rotate();
}
}
};
Static members are like a shared bulletin board in an office - all employees (objects) can see and use the same information!
class BankAccount {
private:
string accountNumber;
double balance;
static int totalAccounts; // Shared by all accounts
static double totalBalance; // Track total money in bank
public:
BankAccount(string accNum, double initialBalance) {
accountNumber = accNum;
balance = initialBalance;
totalAccounts++; // Increment shared counter
totalBalance += balance; // Add to total
}
~BankAccount() {
totalAccounts--; // Decrement when destroyed
totalBalance -= balance;
}
void deposit(double amount) {
balance += amount;
totalBalance += amount; // Update total
}
void withdraw(double amount) {
if (amount <= balance) {
balance -= amount;
totalBalance -= amount;
}
}
// Static method - can be called without object
static int getTotalAccounts() {
return totalAccounts;
}
static double getTotalBalance() {
return totalBalance;
}
};
// Initialize static members outside class
int BankAccount::totalAccounts = 0;
double BankAccount::totalBalance = 0;
// Usage
int main() {
cout << "Accounts: " << BankAccount::getTotalAccounts() << endl; // 0
BankAccount acc1("123", 1000);
BankAccount acc2("456", 2000);
cout << "Accounts: " << BankAccount::getTotalAccounts() << endl; // 2
cout << "Total balance: $" << BankAccount::getTotalBalance() << endl; // 3000
return 0;
}
Operator overloading lets your objects work with operators like +, -, ==, just like built-in types!
class Vector2D {
private:
double x, y;
public:
Vector2D(double x = 0, double y = 0) : x(x), y(y) {}
// Addition operator
Vector2D operator+(const Vector2D& other) const {
return Vector2D(x + other.x, y + other.y);
}
// Subtraction operator
Vector2D operator-(const Vector2D& other) const {
return Vector2D(x - other.x, y - other.y);
}
// Scalar multiplication
Vector2D operator*(double scalar) const {
return Vector2D(x * scalar, y * scalar);
}
// Equality operator
bool operator==(const Vector2D& other) const {
return (x == other.x && y == other.y);
}
// Stream insertion operator (friend function)
friend ostream& operator<<(ostream& out, const Vector2D& v) {
out << "(" << v.x << ", " << v.y << ")";
return out;
}
double magnitude() const {
return sqrt(x * x + y * y);
}
};
// Usage
int main() {
Vector2D v1(3, 4);
Vector2D v2(1, 2);
Vector2D v3 = v1 + v2; // (4, 6)
Vector2D v4 = v1 - v2; // (2, 2)
Vector2D v5 = v1 * 2; // (6, 8)
cout << "v1 = " << v1 << endl;
cout << "v3 = " << v3 << endl;
cout << "Magnitude of v1: " << v1.magnitude() << endl; // 5
return 0;
}
Create a complete banking system with the following features:
class Transaction {
private:
string type; // "Deposit", "Withdrawal", "Transfer"
double amount;
string date;
public:
// TODO: Constructor and display method
};
class Account {
private:
string accountNumber;
double balance;
vector history;
public:
// TODO: Constructor, deposit, withdraw, getBalance
// TODO: addTransaction, showHistory
};
class Customer {
private:
string name;
string customerId;
vector accounts;
public:
// TODO: Constructor, addAccount, getTotalBalance
// TODO: transfer between accounts
};
Friends are like trusted individuals who have keys to your house - they can access private members!
Design classes for a simple 2D game:
class GameObject {
protected:
double x, y; // Position
double vx, vy; // Velocity
double width, height; // Size
public:
virtual void update(double deltaTime);
virtual void render();
bool collidesWith(const GameObject& other);
};
class Player : public GameObject {
private:
int health;
int score;
public:
void handleInput();
void takeDamage(int damage);
};