Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MicrosoftDocs/cpp-docs/llms.txt

Use this file to discover all available pages before exploring further.

Classes and structs are the primary mechanisms in C++ for defining your own types. They bundle data members and member functions together, allowing you to model the state and behavior of real-world entities. In C++, class and struct are nearly identical constructs — the only difference is that struct members and bases default to public accessibility while class members and bases default to private. This page covers class definitions, member functions, constructors and destructors, inheritance, virtual functions, access control, friend declarations, and the Rule of Five for resource-managing classes.

Defining a Class or Struct

A class or struct definition introduces a new type. Members can be data members, member functions, nested types, or static members:
class Box {
public:
    // Constructors
    Box() : m_width(0), m_length(0), m_height(0) {}
    Box(int width, int length, int height)
        : m_width(width), m_length(length), m_height(height) {}

    // Member function
    int volume() const { return m_width * m_length * m_height; }

    // Static member function
    static Box unit() { return Box(1, 1, 1); }

private:
    int m_width;
    int m_length;
    int m_height;
};

// Usage
Box b(3, 4, 5);
std::cout << b.volume();   // 60
In structs, the default accessibility is public. Structs are commonly used for simple data aggregates that carry no invariants, while classes are used when you need to enforce encapsulation via private members.

Access Specifiers

SpecifierWho Can Access
publicAny code that can see the class
protectedThe class itself and its derived classes
privateOnly the class itself (and friend declarations)
class BankAccount {
public:
    explicit BankAccount(double initial) : m_balance(initial) {}
    void     deposit(double amount)  { m_balance += amount; }
    bool     withdraw(double amount);
    double   balance() const { return m_balance; }

protected:
    void audit_log(const std::string& msg);  // accessible to derived classes

private:
    double m_balance;   // hidden from outside world
};

Constructors

A constructor initializes the class members when an object is created. The member initializer list is more efficient than assigning values in the constructor body, because it initializes members directly rather than default-constructing them first:
class Box {
public:
    // Default constructor
    Box() {}

    // Single-parameter constructor with explicit to prevent implicit conversion
    explicit Box(int i) : m_width(i), m_length(i), m_height(i) {}

    // Multi-parameter constructor
    Box(int width, int length, int height)
        : m_width(width), m_length(length), m_height(height) {}

    int volume() { return m_width * m_length * m_height; }

private:
    int m_width{0};
    int m_length{0};
    int m_height{0};
};

int main() {
    Box b1;               // default constructor
    Box b2{5};            // Box(int) — uniform initialization
    Box b3{5, 8, 12};     // Box(int, int, int)
    Box b4(2, 4, 6);      // function-style
}

Delegating Constructors (C++11)

A constructor can delegate to another constructor in the same class, reducing code duplication:
class Box {
public:
    Box() {}
    Box(int i) : Box(i, i, i) {}              // delegates to 3-param constructor
    Box(int w, int l, int h)
        : m_width(w), m_length(l), m_height(h) {}
private:
    int m_width{0}, m_length{0}, m_height{0};
};

Destructors

A destructor is called automatically when an object goes out of scope or is explicitly deleted. It has no parameters and no return type:
class ResourceHolder {
public:
    explicit ResourceHolder(size_t n)
        : m_data(new int[n]), m_size(n) {
        std::cout << "Acquired " << n << " ints\n";
    }

    ~ResourceHolder() {
        delete[] m_data;
        std::cout << "Released\n";
    }

private:
    int*   m_data;
    size_t m_size;
};
// Destructor runs automatically when ResourceHolder goes out of scope

The Rule of Five

If a class manually manages a resource (heap memory, file handles, sockets, etc.), it typically needs to define all five special member functions. This is the Rule of Five:
  1. Destructor
  2. Copy constructor
  3. Copy assignment operator
  4. Move constructor (C++11)
  5. Move assignment operator (C++11)
class MemoryBlock {
public:
    // Constructor (not one of the five special members)
    explicit MemoryBlock(size_t length)
        : _length(length), _data(new int[length]) {}

    // 1. Destructor
    ~MemoryBlock() { delete[] _data; }

    // 2. Copy constructor — deep copy
    MemoryBlock(const MemoryBlock& other)
        : _length(other._length), _data(new int[other._length]) {
        std::copy(other._data, other._data + _length, _data);
    }

    // 3. Copy assignment operator
    MemoryBlock& operator=(const MemoryBlock& other) {
        if (this != &other) {
            delete[] _data;
            _length = other._length;
            _data   = new int[_length];
            std::copy(other._data, other._data + _length, _data);
        }
        return *this;
    }

    // 4. Move constructor — steal resources, leave source empty
    MemoryBlock(MemoryBlock&& other) noexcept
        : _data(nullptr), _length(0) {
        _data         = other._data;
        _length       = other._length;
        other._data   = nullptr;
        other._length = 0;
    }

    // 5. Move assignment operator
    MemoryBlock& operator=(MemoryBlock&& other) noexcept {
        if (this != &other) {
            delete[] _data;
            _data         = other._data;
            _length       = other._length;
            other._data   = nullptr;
            other._length = 0;
        }
        return *this;
    }

    size_t length() const { return _length; }

private:
    size_t _length;
    int*   _data;
};
In practice, prefer using std::unique_ptr or std::vector to manage resources — this makes the compiler-generated special members correct by default and you can follow the Rule of Zero: define none of the five special functions if your members already manage themselves.

Inheritance

A derived class inherits the members of a base class and can extend or override them:
class Shape {
public:
    Shape(const std::string& name) : m_name(name) {}
    virtual double area() const = 0;    // pure virtual — must override
    virtual void   describe() const {
        std::cout << m_name << " area=" << area() << "\n";
    }
    virtual ~Shape() {}                 // virtual destructor is essential
protected:
    std::string m_name;
};

class Circle : public Shape {
public:
    Circle(double r) : Shape("Circle"), m_radius(r) {}
    double area() const override { return 3.14159 * m_radius * m_radius; }
private:
    double m_radius;
};

class Rectangle : public Shape {
public:
    Rectangle(double w, double h) : Shape("Rectangle"), m_width(w), m_height(h) {}
    double area() const override { return m_width * m_height; }
private:
    double m_width, m_height;
};
Always declare the destructor of a polymorphic base class as virtual. Without a virtual destructor, deleting a derived object through a base pointer calls only the base class destructor, leaking the derived class’s resources.

Virtual Functions and Polymorphism

Virtual functions enable runtime polymorphism: the correct overriding function is called based on the actual (dynamic) type of the object, not the static type of the pointer or reference:
void printArea(const Shape& shape) {
    shape.describe();   // calls the correct override at runtime
}

int main() {
    Circle    c(5.0);
    Rectangle r(3.0, 4.0);

    printArea(c);   // Circle area=78.5398
    printArea(r);   // Rectangle area=12

    // Polymorphism through pointers
    std::vector<std::unique_ptr<Shape>> shapes;
    shapes.push_back(std::make_unique<Circle>(2.0));
    shapes.push_back(std::make_unique<Rectangle>(5.0, 3.0));

    for (const auto& s : shapes) s->describe();
}
The override specifier (C++11) makes it a compile error if the function does not actually override a virtual function in the base. The final specifier prevents further overriding or inheritance:
class Ellipse : public Shape {
public:
    double area() const override final { /* ... */ }  // no further override
};

class FixedShape final : public Shape { /* no further derivation */ };

Abstract Classes

A class with at least one pure virtual function (declared with = 0) is an abstract class. It cannot be instantiated directly — it acts as an interface that derived classes must implement:
class Printable {
public:
    virtual void print(std::ostream& os) const = 0;
    virtual ~Printable() = default;
};

class Document : public Printable {
public:
    void print(std::ostream& os) const override {
        os << m_content;
    }
private:
    std::string m_content;
};

Friend Declarations

A friend declaration grants a non-member function or another class access to private and protected members:
class Matrix {
public:
    Matrix(int rows, int cols);

    // friend function can access private members
    friend Matrix operator+(const Matrix& a, const Matrix& b);

    // friend class has full access
    friend class MatrixSerializer;

private:
    std::vector<double> m_data;
    int m_rows, m_cols;
};

Matrix operator+(const Matrix& a, const Matrix& b) {
    // Can access a.m_data, b.m_data directly
}

Static Members

Static data members are shared across all instances of a class; static member functions operate without a this pointer:
class Counter {
public:
    Counter() { ++s_count; }
    ~Counter() { --s_count; }
    static int count() { return s_count; }

private:
    static int s_count;  // declaration
};

int Counter::s_count = 0;  // definition (in .cpp file)

int main() {
    Counter a, b, c;
    std::cout << Counter::count();  // 3
}

Struct vs. Class Comparison

Featurestructclass
Default member accesspublicprivate
Default inheritancepublicprivate
Can have constructorsYesYes
Can have virtual functionsYesYes
Typical useData aggregates, POD typesFull encapsulation, OOP

See Also

Build docs developers (and LLMs) love