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
| Specifier | Who Can Access |
|---|
public | Any code that can see the class |
protected | The class itself and its derived classes |
private | Only 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:
- Destructor
- Copy constructor
- Copy assignment operator
- Move constructor (C++11)
- 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
| Feature | struct | class |
|---|
| Default member access | public | private |
| Default inheritance | public | private |
| Can have constructors | Yes | Yes |
| Can have virtual functions | Yes | Yes |
| Typical use | Data aggregates, POD types | Full encapsulation, OOP |
See Also