Memory Management¶
Managing memory efficiently and safely is one of C++'s core strengths. Modern C++ automates much of this using RAII and Smart Pointers, making manual new and delete largely obsolete.
Stack vs. Heap¶
Understanding where your data lives is crucial.
The Stack¶
- Fast: Allocation is just moving a pointer.
- Automatic: Variables are destroyed when they go out of scope.
- Limited Size: Large arrays can cause a stack overflow.
The Heap (Free Store)¶
- Flexible: You control the lifetime.
- Large: Limited only by system memory.
- Slower: Allocation involves finding free blocks.
- Manual Management: Requires explicit cleanup (or smart pointers).
Pointers vs. References¶
Both refer to data elsewhere in memory, but they have key differences.
References (&)¶
- Must be initialized when declared.
- Cannot be null.
- Cannot be reseated (cannot refer to a different object later).
- Syntax: Use like a normal variable.
Pointers (*)¶
- Can be uninitialized (dangerous!).
- Can be null (
nullptr). - Can be reseated.
- Syntax: Use
*to dereference (access value) and->to access members.
RAII (Resource Acquisition Is Initialization)¶
This is the most important idiom in C++. It binds the life cycle of a resource (memory, file, lock) to the life cycle of an object.
- Constructor: Acquires the resource.
- Destructor: Releases the resource.
Because stack objects are destroyed automatically, RAII guarantees no resource leaks, even if exceptions are thrown.
Smart Pointers (Modern C++)¶
Never use new and delete directly unless you are writing a low-level data structure. Use smart pointers from <memory>.
std::unique_ptr¶
- Exclusive ownership: Only one pointer owns the object.
- Zero overhead: Same size and speed as a raw pointer.
- Transferable: Can be moved (
std::move), but not copied.
std::shared_ptr¶
- Shared ownership: Multiple pointers can own the same object.
- Reference counting: The object is deleted only when the last
shared_ptris destroyed. - Overhead: Slightly slower due to reference counting.
std::weak_ptr¶
- Non-owning observer: Points to an object managed by
shared_ptrwithout increasing the reference count. - Prevents cyclic references: Essential for tree/graph structures.
Move Semantics (C++11)¶
Move semantics allow resources to be transferred rather than copied. This is a massive performance optimization.
Lvalues vs. Rvalues¶
- Lvalue: Has a name and an address (e.g.,
x). - Rvalue: Temporary value (e.g.,
10,x + y, return value of a function).
std::move¶
Casts an lvalue to an rvalue, enabling the move constructor/assignment.
Deep Copy vs. Shallow Copy¶
- Shallow Copy: Copies pointer values. Both objects point to the same memory. (Dangerous if not handled carefully).
- Deep Copy: Allocates new memory and copies the actual data. (Safer but slower).
Smart pointers and STL containers handle this correctly automatically.
Best Practices¶
- Prefer Stack: Use stack variables whenever possible.
- Use
std::unique_ptr: By default for heap allocation. - Use
std::shared_ptr: Only when ownership is truly shared. - Avoid
new/delete: They are error-prone.