Object-Oriented Programming

Classes and Objects

Master C++ classes — member variables, member functions, access specifiers, the this pointer, static members, and object-oriented design fundamentals.

Classes and Objects

A class is a blueprint that defines the data (member variables) and behaviour (member functions/methods) of objects. An object is an instance of a class — a concrete thing built from that blueprint. Classes are the foundation of object-oriented programming in C++.


Defining a Class

cpp
#include <iostream>
#include <string>
using namespace std;

class BankAccount {
private:                          // Access specifier — hidden from outside
    string owner;
    double balance;
    int    accountNumber;

public:                           // Accessible from outside
    // Constructor — initialises the object
    BankAccount(const string& ownerName, double initialBalance, int accNum) {
        owner         = ownerName;
        balance       = initialBalance;
        accountNumber = accNum;
    }

    // Member functions (methods)
    void deposit(double amount) {
        if (amount > 0) balance += amount;
    }

    bool withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            return true;
        }
        return false;
    }

    double getBalance() const {   // const — does not modify the object
        return balance;
    }

    void print() const {
        cout << "Account #" << accountNumber
             << " (" << owner << "): ₹" << balance << "\n";
    }
};

int main() {
    BankAccount acc("Alice", 5000.0, 1001);
    acc.deposit(2500.0);
    acc.withdraw(1000.0);
    acc.print();
    // Account #1001 (Alice): ₹6500

    return 0;
}

Access Specifiers

SpecifierAccessible from
publicAnywhere — the class's interface
privateOnly from within the class (default for class)
protectedWithin the class and its subclasses

struct in C++ is identical to class except all members are public by default. Use struct for plain data containers; use class for types with invariants (rules about the data).


The this Pointer

Inside any member function, this is a pointer to the current object:

cpp
#include <iostream>
#include <string>
using namespace std;

class Counter {
    int count;
    string name;

public:
    Counter(int count, const string& name) {
        // Parameter name shadows member name — use 'this->' to distinguish
        this->count = count;
        this->name  = name;
    }

    // Method chaining — return *this to allow c.inc().inc().inc()
    Counter& increment() {
        count++;
        return *this;
    }

    Counter& add(int n) {
        count += n;
        return *this;
    }

    void print() const {
        cout << name << ": " << count << "\n";
    }
};

int main() {
    Counter c(0, "clicks");
    c.increment().increment().add(5).increment();
    c.print();   // clicks: 8

    return 0;
}

Const Member Functions

A const member function promises not to modify the object. It can be called on const objects:

cpp
class Rectangle {
    double width, height;

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

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

    void scale(double factor) {          // Non-const — modifies the object
        width  *= factor;
        height *= factor;
    }
};

void printInfo(const Rectangle& r) {
    cout << "Area: " << r.area() << "\n";       // OK — const method
    // r.scale(2.0);  // ERROR — cannot call non-const method on const ref
}

Static Members

static members belong to the class, not to individual objects:

cpp
#include <iostream>
#include <string>
using namespace std;

class Employee {
    static int nextId;       // Shared across all Employee objects
    int    id;
    string name;
    double salary;

public:
    Employee(const string& name, double salary)
        : id(nextId++), name(name), salary(salary) {}

    static int getCount() { return nextId - 1; }  // Static method

    void print() const {
        cout << "ID " << id << ": " << name << " (₹" << salary << ")\n";
    }
};

// Static member must be defined outside the class
int Employee::nextId = 1;

int main() {
    Employee e1("Alice", 75000);
    Employee e2("Bob",   65000);
    Employee e3("Charlie", 80000);

    e1.print();  // ID 1: Alice (₹75000)
    e2.print();  // ID 2: Bob (₹65000)
    e3.print();  // ID 3: Charlie (₹80000)

    cout << "Total employees: " << Employee::getCount() << "\n";  // 3

    return 0;
}

Getters and Setters

Encapsulation means hiding data and providing controlled access:

cpp
#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;

class Temperature {
    double celsius;   // Private — in Celsius internally

public:
    Temperature(double c = 0.0) {
        setCelsius(c);   // Use setter to validate on construction too
    }

    // Getter
    double getCelsius()    const { return celsius; }
    double getFahrenheit() const { return celsius * 9.0 / 5.0 + 32; }
    double getKelvin()     const { return celsius + 273.15; }

    // Setter with validation
    void setCelsius(double c) {
        if (c < -273.15) {
            throw invalid_argument("Temperature below absolute zero!");
        }
        celsius = c;
    }

    void setFahrenheit(double f) {
        setCelsius((f - 32) * 5.0 / 9.0);
    }
};

int main() {
    Temperature t(100.0);
    cout << t.getCelsius()    << "°C\n";   // 100°C
    cout << t.getFahrenheit() << "°F\n";   // 212°F
    cout << t.getKelvin()     << "K\n";    // 373.15K

    t.setFahrenheit(32.0);
    cout << t.getCelsius() << "°C\n";      // 0°C

    try {
        t.setCelsius(-300);   // Throws!
    } catch (const invalid_argument& e) {
        cout << "Error: " << e.what() << "\n";
    }

    return 0;
}

Separating Declaration from Implementation

In real projects, class declarations go in .h (header) files and definitions go in .cpp files:

cpp
// rectangle.h
#pragma once
class Rectangle {
    double width, height;
public:
    Rectangle(double w, double h);
    double area()      const;
    double perimeter() const;
    void   scale(double factor);
};

// rectangle.cpp
#include "rectangle.h"

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

double Rectangle::area()      const { return width * height; }
double Rectangle::perimeter() const { return 2 * (width + height); }
void   Rectangle::scale(double factor) {
    width  *= factor;
    height *= factor;
}

Practical Example: Student Grade Manager

cpp
#include <iostream>
#include <vector>
#include <string>
#include <numeric>
#include <algorithm>
using namespace std;

class Student {
    string         name;
    vector<double> grades;

public:
    Student(const string& name) : name(name) {}

    void addGrade(double grade) {
        if (grade >= 0 && grade <= 100)
            grades.push_back(grade);
    }

    double average() const {
        if (grades.empty()) return 0.0;
        return accumulate(grades.begin(), grades.end(), 0.0) / grades.size();
    }

    double highest() const {
        if (grades.empty()) return 0.0;
        return *max_element(grades.begin(), grades.end());
    }

    double lowest() const {
        if (grades.empty()) return 0.0;
        return *min_element(grades.begin(), grades.end());
    }

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

    const string& getName() const { return name; }

    void printReport() const {
        cout << "Student: " << name << "\n";
        cout << "  Grades:  ";
        for (double g : grades) cout << g << " ";
        cout << "\n";
        cout << "  Average: " << average()     << "\n";
        cout << "  Highest: " << highest()     << "\n";
        cout << "  Lowest:  " << lowest()      << "\n";
        cout << "  Grade:   " << letterGrade() << "\n";
    }
};

class Classroom {
    string         className;
    vector<Student> students;

public:
    Classroom(const string& name) : className(name) {}

    void addStudent(Student s) {
        students.push_back(move(s));
    }

    double classAverage() const {
        if (students.empty()) return 0.0;
        double total = 0.0;
        for (const auto& s : students) total += s.average();
        return total / students.size();
    }

    void printReport() const {
        cout << "=== " << className << " ===\n\n";
        for (const auto& s : students) {
            s.printReport();
            cout << "\n";
        }
        cout << "Class average: " << classAverage() << "\n";
    }
};

int main() {
    Classroom cls("C++ Fundamentals");

    Student alice("Alice");
    alice.addGrade(92); alice.addGrade(88); alice.addGrade(95);

    Student bob("Bob");
    bob.addGrade(75); bob.addGrade(82); bob.addGrade(78);

    Student charlie("Charlie");
    charlie.addGrade(60); charlie.addGrade(55); charlie.addGrade(70);

    cls.addStudent(alice);
    cls.addStudent(bob);
    cls.addStudent(charlie);
    cls.printReport();
    // === C++ Fundamentals ===
    //
    // Student: Alice
    //   Grades:  92 88 95
    //   Average: 91.6667
    //   Grade:   A
    // ...
    // Class average: 77.2222

    return 0;
}

Summary

  • A class bundles data (member variables) and behaviour (member functions) into one type
  • private members are hidden — only accessible from within the class; public members form the interface
  • const member functions cannot modify the object — mark all read-only methods as const
  • The this pointer refers to the current object; return *this to enable method chaining
  • static members belong to the class itself, not to individual objects; access them via ClassName::member
  • Separate class declarations (.h) from definitions (.cpp) for maintainable, reusable code
  • Use getters/setters to validate data before accepting it — this is the key benefit of encapsulation

Next, we'll learn about constructors and destructors — how objects are built and cleaned up in C++.