Classes and Objects: Building Your Own Types

What is Object-Oriented Programming?

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!

Classes: Blueprints for Objects

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!

graph TD A[Class: Blueprint] --> B[Defines Structure] A --> C[Defines Behavior] B --> D[Properties/Attributes] C --> E[Methods/Functions] A --> F[Object: Instance] F --> G[Actual Data] F --> H[Can Use Methods] style A fill:#E3F2FD style F fill:#FFE4B5

Creating Your First Class

Anatomy of a Class class Rectangle private: double width; double height; public: Rectangle(double w, double h); double getArea(); double getPerimeter(); void setDimensions(double w, double h); Hidden Visible

Complete Rectangle Class Example

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: Protecting Your Data

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 and Destructors

Constructors are like a welcome party when an object is born, and destructors are the cleanup crew when it's time to go!

graph LR A[Object Lifecycle] --> B[Constructor Called] B --> C[Object Lives] C --> D[Destructor Called] B --> E[Initialize Data] B --> F[Allocate Resources] D --> G[Clean Up] D --> H[Free Resources] style B fill:#4CAF50 style D fill:#F44336

Constructor and Destructor Examples

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

The 'this' pointer is like saying "myself" - it's how an object refers to itself!

Class Composition: Objects Within Objects

Composition is like building with LEGO blocks - you create complex objects by combining simpler ones!

Class Composition Example Date month: int day: int year: int Time hour: int minute: int second: int Event name: string location: string date: Date time: Time Event "has a" Date and Time

Composition Example

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: Shared Among All Objects

Static members are like a shared bulletin board in an office - all employees (objects) can see and use the same information!

Static Members Example

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: Making Objects Natural

Operator overloading lets your objects work with operators like +, -, ==, just like built-in types!

graph TD A[Operator Overloading] --> B[Binary Operators] A --> C[Unary Operators] A --> D[Comparison Operators] A --> E[Stream Operators] B --> F["+ - * /"] C --> G["++ -- -"] D --> H["== != < >"] E --> I["<< >>"]
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;
}

Practice Exercise: Bank System

Build a Banking System

Create a complete banking system with the following features:

  1. Account class with deposit, withdraw, transfer
  2. Customer class that can have multiple accounts
  3. Transaction history
  4. Static member to track total bank assets
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
};
Implementation Hints

Friend Functions and Classes

Friends are like trusted individuals who have keys to your house - they can access private members!

Friend Access Class A Private Data 🔒 Locked Friend Function Has Access! 🔑

Challenge Exercise: Game Development

Create a Simple Game Framework

Design classes for a simple 2D game:

  1. GameObject base class with position and velocity
  2. Player class with health and score
  3. Enemy class with AI behavior
  4. Collision detection between objects
  5. Game class to manage all objects
Class Structure Hints
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);
};

Key Takeaways

graph LR A[Master OOP] --> B[Design Better Code] B --> C[Create Reusable Components] C --> D[Build Complex Systems] D --> E[Professional Software Engineer!]