Skip to content

Classes & Objects

Classes are the heart of Object-Oriented Programming (OOP) in C++. They encapsulate data and behavior into a single unit.

Class vs. Struct

In C++, class and struct are almost identical. - class: Members are private by default. - struct: Members are public by default.

Use struct for simple data containers (PODs) and class for objects with invariants and logic.

struct Point {
    int x;
    int y;
};

class Account {
    double balance; // private
public:
    void deposit(double amount);
};

Access Specifiers

  • public: Accessible from anywhere.
  • private: Accessible only from within the class.
  • protected: Accessible from within the class and derived classes.
1
2
3
4
5
6
7
8
class Sensor {
public:
    void read() { /* ... */ } // Public interface

private:
    int raw_value; // Internal data
    void calibrate() { /* ... */ } // Internal helper
};

Static Members

Static members belong to the class itself, not to any specific object instance.

Static Variables

Shared across all instances. Must be defined outside the class (unless inline or constexpr).

1
2
3
4
5
6
7
8
class Counter {
public:
    static int count;
    Counter() { count++; }
};

// Definition
int Counter::count = 0;

Static Functions

Can be called without an object. Can only access static variables.

1
2
3
4
5
6
class Math {
public:
    static int add(int a, int b) { return a + b; }
};

int sum = Math::add(1, 2);

Unions

A union is a special class type where all members share the same memory location. Only one member can be active at a time.

1
2
3
4
5
6
7
8
9
union Data {
    int i;
    float f;
    char c;
};

Data d;
d.i = 10;
// d.f is now garbage (or a reinterpretation of the bits of 10)

Modern C++: Prefer std::variant over union for type safety.

Constructors & Destructors

  • Constructor: Called when an object is created. Used to initialize invariants.
  • Destructor: Called when an object is destroyed. Used to release resources (RAII).
1
2
3
4
5
6
7
8
9
class GPIO {
public:
    GPIO(int pin) {
        // Open GPIO pin
    }
    ~GPIO() {
        // Close GPIO pin
    }
};

Member Initializers (Modern C++)

In Modern C++, you can (and should) initialize members where they are declared. This ensures they always have a valid default value.

1
2
3
4
5
6
7
8
class User {
    std::string name = "Unknown"; // Default value
    int age = 0;                  // Default value

public:
    User(const std::string& n, int a) : name(n), age(a) {}
    User() = default; // Uses the in-class initializers
};

Const Member Functions

If a member function does not modify the object, mark it as const. This allows it to be called on const objects.

1
2
3
4
5
6
7
class Circle {
    double radius = 1.0;
public:
    double get_area() const { // Promise not to change radius
        return 3.14 * radius * radius;
    }
};

The Rule of Zero (Best Practice)

The best way to manage resources is not to manage them manually. If your class relies on standard types like std::string, std::vector, or std::unique_ptr, you do not need to write a destructor, copy constructor, or assignment operator. The compiler generates them correctly for you.

1
2
3
4
5
class Player {
    std::string name;
    std::vector<int> scores;
    // No destructor needed! Memory is handled automatically.
};

The Rule of Five

Only if you are writing a low-level RAII wrapper (e.g., handling a raw file handle), you need to implement all five special members:

  1. Destructor
  2. Copy Constructor
  3. Copy Assignment Operator
  4. Move Constructor
  5. Move Assignment Operator

Prefer Rule of Zero whenever possible.