Tutorial

C++ for Beginners Part 7: Classes and Object-Oriented Programming

Object-oriented programming lets you model the real world as objects with data and behavior. This article covers C++ classes from the ground up: member variables, member functions, constructors, destructors, access specifiers, and the principle of encapsulation — with a full BankAccount example.


SERIES

C++ for Beginners: A Complete Guide

An eight-part beginner-friendly journey through C++: from writing your first program and understanding variables, through control flow, functions, and arrays, to the heart of C++ — pointers, classes, inheritance, and polymorphism. Each article is self-contained and builds on the previous, giving you both a quick reference and a progressive path to mastery.

Table of Contents

What is OOP?

Object-Oriented Programming (OOP) organises code around objects — bundles of data (attributes) and behavior (methods). Instead of a loose collection of functions, you model the problem as interacting objects.

The four OOP pillars:

  1. Encapsulation — hiding internal details, exposing only what's needed
  2. Inheritance — deriving new types from existing ones (Part 8)
  3. Polymorphism — different types responding to the same interface (Part 8)
  4. Abstraction — working with concepts, not implementations

Defining a class

C++
class Rectangle {
public:
    double width;
    double height;

    double area() {
        return width * height;
    }

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

A class defines a blueprint. An object is an instance of that blueprint:

C++
Rectangle r;          // create an object
r.width  = 5.0;
r.height = 3.0;
std::cout << r.area();        // 15
std::cout << r.perimeter();   // 16

Access specifiers

C++ has three access levels:

Specifier Who can access
public Anyone
private Only class members
protected Class members and derived classes

Members are private by default in a class (unlike struct where they're public by default).

Encapsulation in practice

C++
class BankAccount {
private:
    std::string owner;
    double balance;

public:
    // Constructor
    BankAccount(std::string name, double initialBalance) {
        owner   = name;
        balance = (initialBalance >= 0) ? initialBalance : 0;
    }

    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 {
        return balance;
    }

    void print() const {
        std::cout << owner << ": $" << balance << "
";
    }
};

balance is private — outside code cannot set it to a negative number or modify it directly. All changes go through deposit() and withdraw(), which enforce business rules.

C++
BankAccount acc("Alice", 1000.0);
acc.deposit(250.0);
acc.withdraw(100.0);
acc.print();               // Alice: $1150
// acc.balance = -5000;   // compile error: balance is private

Constructors

A constructor is a special function called automatically when an object is created. It has the same name as the class and no return type:

C++
class Point {
public:
    double x, y;

    // Default constructor — no arguments
    Point() {
        x = 0;
        y = 0;
    }

    // Parameterized constructor
    Point(double x, double y) {
        this->x = x;   // 'this->' distinguishes the member from the parameter
        this->y = y;
    }
};

Point p1;              // calls default constructor → (0, 0)
Point p2(3.0, 4.0);   // calls parameterized constructor → (3, 4)

Member initializer list (preferred)

Instead of assigning in the body, use the initializer list — it's more efficient and required for const members:

C++
Point(double x, double y) : x(x), y(y) {}

The this pointer

Inside any member function, this is a pointer to the current object. Use it when a parameter name shadows a member name:

C++
class Circle {
    double radius;
public:
    void setRadius(double radius) {
        this->radius = radius;   // this->radius is the member
    }
};

const member functions

A const member function promises not to modify the object. Call it on const objects:

C++
class Point {
public:
    double x, y;
    Point(double x, double y) : x(x), y(y) {}

    double distanceFromOrigin() const {
        return std::sqrt(x*x + y*y);
    }
};

const Point p(3.0, 4.0);
std::cout << p.distanceFromOrigin();   // 5
// p.x = 1;  // error: cannot modify const object

Mark any member function that doesn't modify state as const. It's a safety guarantee and allows the function to work on const objects.


Destructors

A destructor runs automatically when an object is destroyed (goes out of scope or is deleted). Used to release resources:

C++
class FileWriter {
    std::ofstream file;
public:
    FileWriter(const std::string& filename) {
        file.open(filename);
    }

    ~FileWriter() {           // destructor — note the ~
        if (file.is_open()) {
            file.close();
            std::cout << "File closed.
";
        }
    }

    void write(const std::string& text) {
        file << text;
    }
};

{
    FileWriter fw("log.txt");
    fw.write("Hello
");
}   // fw goes out of scope here → destructor called → file closed

Static members

static members belong to the class itself, not to any instance. All objects share one copy:

C++
class Counter {
public:
    static int count;

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

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

int Counter::count = 0;   // must define static member outside class

Counter a, b, c;
std::cout << Counter::getCount();   // 3

{
    Counter d;
    std::cout << Counter::count;    // 4
}
std::cout << Counter::count;        // 3 (d was destroyed)

Separating declaration from definition

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

point.h

C++
#pragma once
#include <cmath>

class Point {
public:
    double x, y;
    Point(double x, double y);
    double distanceTo(const Point& other) const;
    void print() const;
};

point.cpp

C++
#include "point.h"
#include <iostream>

Point::Point(double x, double y) : x(x), y(y) {}

double Point::distanceTo(const Point& other) const {
    double dx = x - other.x;
    double dy = y - other.y;
    return std::sqrt(dx*dx + dy*dy);
}

void Point::print() const {
    std::cout << "(" << x << ", " << y << ")
";
}

main.cpp

C++
#include "point.h"

int main() {
    Point p1(0, 0), p2(3, 4);
    p1.print();                         // (0, 0)
    p2.print();                         // (3, 4)
    std::cout << p1.distanceTo(p2);     // 5
}

Practical example: full BankAccount

C++
#include <iostream>
#include <string>
#include <vector>

class BankAccount {
private:
    std::string owner;
    double balance;
    std::vector<std::string> history;

    void log(const std::string& entry) {
        history.push_back(entry);
    }

public:
    BankAccount(const std::string& name, double initial = 0)
        : owner(name), balance(initial >= 0 ? initial : 0) {
        log("Account opened with $" + std::to_string(balance));
    }

    void deposit(double amount) {
        if (amount <= 0) return;
        balance += amount;
        log("Deposited $" + std::to_string(amount));
    }

    bool withdraw(double amount) {
        if (amount <= 0 || amount > balance) return false;
        balance -= amount;
        log("Withdrew $" + std::to_string(amount));
        return true;
    }

    double getBalance() const { return balance; }

    void printStatement() const {
        std::cout << "=== " << owner << "'s Account ===
";
        for (const auto& entry : history) std::cout << "  " << entry << "
";
        std::cout << "  Balance: $" << balance << "
";
    }
};

int main() {
    BankAccount acc("Bob", 500.0);
    acc.deposit(250.0);
    acc.withdraw(100.0);
    acc.withdraw(1000.0);   // fails silently (insufficient funds)
    acc.printStatement();
}

Output:

Code
=== Bob's Account ===
  Account opened with $500.000000
  Deposited $250.000000
  Withdrew $100.000000
  Balance: $650

Key takeaways

  • A class is a blueprint; an object is an instance.
  • private data with public methods enforces encapsulation.
  • Constructors initialise objects; destructors clean up resources.
  • const member functions cannot modify the object — mark them always.
  • this is a pointer to the current object — use it to disambiguate names.
  • In real projects, split class declarations (.h) from definitions (.cpp).

Next up: inheritance and polymorphism — extending classes and writing code that works with any derived type.


Was this article helpful?

w

webencher Editorial

Software engineers and technical writers with 10+ years of combined experience in algorithms, systems design, and web development. Every article is reviewed for accuracy, depth, and practical applicability.

More by this author →