C++ Memory Management Fundamentals
Memory Management Overview
Memory management in C++ refers to how programs request, use, and release memory during execution. Unlike garbage-collected languages, C++ gives developers explicit control over allocation and deallocation.
Core concepts include:
- Stack vs Heap: Different storage regions with distinct lifetimes
- Manual Allocation: Using `new`/`delete` (and `new[]`/`delete[]`) for dynamic memory
- RAII (Resource Acquisition Is Initialization): Binding resources to object lifetimes
- Smart Pointers: Modern tools for safer memory management
- Common Errors: Memory leaks, dangling pointers, buffer overruns, double delete
Memory Segments in C++
Memory Segment | Description | Characteristics |
---|---|---|
Stack | Automatic storage for local variables | Very fast allocation/deallocation; limited size; automatically released when scope ends |
Heap | Dynamic storage managed by the runtime allocator | Manually managed (or via smart pointers/containers); allocation/deallocation typically slower than stack; access speed depends on locality/caching, not on 'being heap' |
Global/Static | Holds objects with static storage duration (globals, `static` variables) | Allocated once and persists for the program lifetime |
Code (Text) | Executable program instructions | Generally read-only/executable; laid out by the toolchain and loaded by the OS |
Memory-mapped | OS-provided mappings (e.g., files/devices) exposed as memory | Platform-dependent; useful for I/O and IPC |
Stack vs Heap Memory
Stack objects are reclaimed automatically when they go out of scope. Heap allocations provide flexibility but require correct and matching deallocation.
#include <iostream>
using namespace std;
void stackExample() {
int stackVar = 42; // automatic storage (stack)
cout << "Stack variable: " << stackVar << '\n';
}
void heapExample() {
int* heapVar = new int(42); // dynamic storage (heap)
cout << "Heap variable: " << *heapVar << '\n';
delete heapVar; // must be freed manually (matches new)
}
int main() {
stackExample();
heapExample();
}
Stack variable: 42 Heap variable: 42
RAII and Smart Pointers (Modern C++)
RAII ties resource lifetime to object lifetime, ensuring release in destructors—even on exceptions. Prefer smart pointers over owning raw pointers:
- `std::unique_ptr
- `std::shared_ptr
- `std::weak_ptr
#include <iostream>
#include <memory>
#include <vector>
int main() {
// Unique ownership
auto p = std::make_unique<int>(42); // no manual delete
std::cout << *p << "\n";
// Shared ownership
auto sp1 = std::make_shared<std::vector<int>>(3, 7);
auto sp2 = sp1; // shares ownership
sp2->push_back(9);
std::cout << sp1->size() << "\n"; // 4
}
Prefer Standard Containers
Instead of manual `new[]` and raw arrays, use containers that manage memory automatically:
- `std::vector`, `std::string`, `std::array`, `std::map`, etc.
These provide bounds-aware operations, exception safety, and fewer lifetime bugs.
#include <vector>
#include <string>
std::vector<int> nums = {1,2,3};
nums.push_back(4);
std::string s = "hello";
s += " world";
Safe Allocation/Deallocation Rules
- Match `new` with `delete` and `new[]` with `delete[]`.
- Never free memory with a different mechanism than it was allocated with.
- `delete nullptr;` is safe and has no effect.
- After manual `delete`, setting the pointer to `nullptr` can help avoid accidental reuse (not needed with smart pointers).
- Be careful with ownership transfer; document who owns and frees a resource.
Common Memory Issues
- Memory Leak: Allocated memory never released
- Dangling Pointer/Reference: Refers to freed or out-of-scope object
- Double Delete: Releasing the same allocation twice
- Buffer Overflow/Underflow: Writing outside allocated bounds
- Fragmentation: Many differently sized allocations causing gaps
- Shared Ownership Cycles: `shared_ptr` cycles that prevent reclamation (use `weak_ptr`)
Debugging & Tools
Use tooling to detect bugs early:
- AddressSanitizer (ASan), UndefinedBehaviorSanitizer (UBSan)
- LeakSanitizer (LSan), ThreadSanitizer (TSan)
- Valgrind or platform-specific heap checkers
Enable runtime checks and compile with warnings elevated where possible.