Docs

README

Classes in C++

Table of Contents

  1. What is a Class
  2. Defining Classes
  3. Members and Methods
  4. Constructors
  5. Destructors
  6. this Pointer
  7. Static Members
  8. Best Practices

What is a Class

A class is a blueprint for creating objects. It encapsulates:

  • Data (member variables / attributes)
  • Behavior (member functions / methods)

Object-Oriented Programming Concepts

ConceptDescription
EncapsulationBundle data and methods together
AbstractionHide implementation details
InheritanceCreate new classes from existing ones
PolymorphismSame interface, different behaviors

Class vs Object

// Class = Blueprint
class Car {
    string brand;
    int speed;
};

// Objects = Instances of the blueprint
Car myCar;      // Object 1
Car yourCar;    // Object 2

Defining Classes

Basic Syntax

class ClassName {
    // Access specifiers
public:
    // Public members (accessible from outside)

private:
    // Private members (only accessible within class)

protected:
    // Protected members (accessible in derived classes)
};  // Semicolon required!

Simple Example

class Rectangle {
public:
    int width;
    int height;

    int area() {
        return width * height;
    }
};

// Usage
Rectangle rect;
rect.width = 10;
rect.height = 5;
cout << rect.area();  // 50

Access Specifiers

class Example {
public:      // Accessible everywhere
    int publicVar;

private:     // Accessible only in this class
    int privateVar;

protected:   // Accessible in this class and derived classes
    int protectedVar;
};

// Default access is private for class, public for struct
class DefaultPrivate {
    int x;  // private by default
};

struct DefaultPublic {
    int x;  // public by default
};

Members and Methods

Member Variables (Data Members)

class Person {
private:
    string name;      // Instance variable
    int age;          // Each object has its own copy

public:
    static int count; // Class variable (shared by all)
};

Member Functions (Methods)

class Calculator {
public:
    // Method declaration
    int add(int a, int b);

    // Inline method (defined inside class)
    int subtract(int a, int b) {
        return a - b;
    }
};

// Method definition outside class
int Calculator::add(int a, int b) {
    return a + b;
}

Const Methods

class Circle {
private:
    double radius;

public:
    // const method - promises not to modify the object
    double getArea() const {
        return 3.14159 * radius * radius;
    }

    // Non-const method - can modify object
    void setRadius(double r) {
        radius = r;
    }
};

// const objects can only call const methods
const Circle c;
c.getArea();     // OK
// c.setRadius(5); // ERROR!

Constructors

Special methods called when an object is created.

Default Constructor

class Point {
public:
    int x, y;

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

Point p;  // Calls Point()

Parameterized Constructor

class Point {
public:
    int x, y;

    // Parameterized constructor
    Point(int xVal, int yVal) {
        x = xVal;
        y = yVal;
    }
};

Point p(10, 20);  // Calls Point(10, 20)

Member Initializer List (Preferred)

class Point {
public:
    int x, y;

    // Initializer list (more efficient)
    Point(int xVal, int yVal) : x(xVal), y(yVal) {
        // Constructor body (optional)
    }
};

Why use initializer lists:

  1. More efficient (direct initialization)
  2. Required for const members
  3. Required for reference members
  4. Required for members without default constructors

Constructor Overloading

class Rectangle {
private:
    int width, height;

public:
    // Default constructor
    Rectangle() : width(0), height(0) {}

    // One parameter (square)
    Rectangle(int size) : width(size), height(size) {}

    // Two parameters
    Rectangle(int w, int h) : width(w), height(h) {}
};

Rectangle r1;        // 0 x 0
Rectangle r2(5);     // 5 x 5
Rectangle r3(3, 4);  // 3 x 4

Default Parameter Values

class Rectangle {
public:
    int width, height;

    Rectangle(int w = 1, int h = 1) : width(w), height(h) {}
};

Rectangle r1;        // 1 x 1
Rectangle r2(5);     // 5 x 1
Rectangle r3(3, 4);  // 3 x 4

Copy Constructor

class Point {
public:
    int x, y;

    Point(int xVal, int yVal) : x(xVal), y(yVal) {}

    // Copy constructor
    Point(const Point& other) : x(other.x), y(other.y) {
        cout << "Copy constructor called" << endl;
    }
};

Point p1(10, 20);
Point p2 = p1;    // Calls copy constructor
Point p3(p1);     // Also calls copy constructor

Delegating Constructors (C++11)

class Rectangle {
private:
    int width, height;

public:
    // Primary constructor
    Rectangle(int w, int h) : width(w), height(h) {}

    // Delegating to primary
    Rectangle() : Rectangle(0, 0) {}
    Rectangle(int size) : Rectangle(size, size) {}
};

Destructors

Called when an object is destroyed.

Basic Destructor

class Resource {
private:
    int* data;

public:
    Resource(int size) {
        data = new int[size];
        cout << "Constructor: allocated memory" << endl;
    }

    // Destructor - same name as class with ~
    ~Resource() {
        delete[] data;
        cout << "Destructor: freed memory" << endl;
    }
};

{
    Resource r(100);  // Constructor called
}  // Destructor called when r goes out of scope

When Destructors Are Called

// 1. Local objects - when scope ends
void foo() {
    MyClass obj;
}  // Destructor called here

// 2. Dynamic objects - when delete is called
MyClass* ptr = new MyClass();
delete ptr;  // Destructor called here

// 3. Array elements - each element's destructor
MyClass arr[3];
// 3 destructors called when arr goes out of scope

// 4. Static objects - when program ends
static MyClass s;  // Destructor at program end

this Pointer

Pointer to the current object instance.

Basic Usage

class Point {
private:
    int x, y;

public:
    Point(int x, int y) {
        // 'this' disambiguates member from parameter
        this->x = x;
        this->y = y;
    }

    // Return reference to self (method chaining)
    Point& setX(int x) {
        this->x = x;
        return *this;
    }

    Point& setY(int y) {
        this->y = y;
        return *this;
    }
};

// Method chaining
Point p(0, 0);
p.setX(10).setY(20);  // Chained calls

Comparing Objects

class Point {
public:
    int x, y;

    bool isSameAs(const Point& other) const {
        return this == &other;  // Compare addresses
    }
};

Static Members

Belong to the class, not to individual objects.

Static Variables

class Counter {
private:
    static int count;  // Shared by all objects
    int id;

public:
    Counter() : id(++count) {}

    int getId() const { return id; }
    static int getCount() { return count; }
};

// Must define static variable outside class
int Counter::count = 0;

Counter c1, c2, c3;
cout << Counter::getCount();  // 3

Static Methods

class Math {
public:
    // Static method - no 'this' pointer
    static int add(int a, int b) {
        return a + b;
    }

    static double PI() {
        return 3.14159265359;
    }
};

// Call without object
int sum = Math::add(5, 3);
double pi = Math::PI();

Static vs Instance Members

class Example {
public:
    int instanceVar;         // Each object has its own
    static int staticVar;    // Shared by all objects

    void instanceMethod() {
        instanceVar = 10;    // OK
        staticVar = 20;      // OK - can access static
    }

    static void staticMethod() {
        // instanceVar = 10; // ERROR! No 'this' pointer
        staticVar = 20;      // OK
    }
};

Best Practices

✅ Do

// 1. Use member initializer lists
class Good {
    int x;
public:
    Good(int val) : x(val) {}  // Preferred
};

// 2. Make methods const when they don't modify state
int getValue() const { return value; }

// 3. Use private for data members
class Encapsulated {
private:
    int data;
public:
    int getData() const { return data; }
    void setData(int d) { data = d; }
};

// 4. Provide destructors for resources
class ResourceOwner {
    int* ptr;
public:
    ResourceOwner() : ptr(new int) {}
    ~ResourceOwner() { delete ptr; }
};

// 5. Follow Rule of Three/Five/Zero
// If you define one of: destructor, copy constructor, copy assignment
// You probably need all three (or five with move semantics)

❌ Don't

// 1. Don't make everything public
class Bad {
public:
    int internalData;  // Should be private
};

// 2. Don't use assignment in constructors when initializer list works
class Inefficient {
    string name;
public:
    Inefficient(const string& n) {
        name = n;  // Default construct then assign
    }
};

// 3. Don't forget virtual destructor in base classes
class Base {
public:
    ~Base() {}  // Should be virtual if inherited
};

// 4. Don't rely on compiler-generated constructors for complex classes
class Complex {
    int* data;  // Needs custom destructor, copy, etc.
};

Quick Reference

class MyClass {
private:
    int data;
    static int count;

public:
    // Constructors
    MyClass();                          // Default
    MyClass(int d);                     // Parameterized
    MyClass(const MyClass& other);      // Copy
    MyClass(MyClass&& other);           // Move (C++11)

    // Destructor
    ~MyClass();

    // Assignment operators
    MyClass& operator=(const MyClass& other);   // Copy assign
    MyClass& operator=(MyClass&& other);        // Move assign

    // Methods
    int getData() const;                 // Const method
    void setData(int d);                 // Non-const method
    static int getCount();               // Static method
};

// Static member definition
int MyClass::count = 0;

// Object creation
MyClass obj1;              // Default constructor
MyClass obj2(42);          // Parameterized constructor
MyClass obj3 = obj2;       // Copy constructor
MyClass obj4(std::move(obj3));  // Move constructor

Compile & Run

g++ -std=c++17 -Wall -Wextra examples.cpp -o examples
./examples
README - C++ Tutorial | DeepML