Last updated: December 13, 2025
Author: Paul Namalomba
- SESKA Computational Engineer
- Software Developer
- PhD Candidate (Civil Engineering Spec. Computational and Applied Mechanics)
Contact: kabwenzenamalomba@gmail.com
Website: paulnamalomba.github.io
This guide explains how C++ (MSVC on Windows) handles key memory and execution concepts: value vs reference semantics, stack vs heap storage, copy semantics (deep vs shallow), and nullability. The content focuses on what actually happens under the hood on Windows using MSVC, with short, runnable examples and Windows-specific notes.
Notes: Use the "Developer Command Prompt for VS" (recommended) which sets up MSVC environment variables. If using PowerShell, run the vcvarsall.bat script from your Visual Studio installation first.
cmd
REM open "Developer Command Prompt for VS"
cl /EHsc main.cpp /Fe:main.exe
main.exe
powershell
& 'C:\Program Files (x86)\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat' x64
cl.exe /EHsc main.cpp /Fe:main.exe
.\main.exe
/EHsc enables C++ exception handling model, optimize flags as needed (/O2).This guide explains how C++ (MSVC on Windows) handles key memory and execution concepts: value vs reference semantics, stack vs heap storage, copy semantics (deep vs shallow), and nullability. The content focuses on what actually happens under the hood on Windows using MSVC, with short, runnable examples and Windows-specific notes.
nullptr, raw pointers, smart pointers, std::optionalC++ does not have a single runtime-managed split of "value vs reference" like some managed languages. Instead:
struct / class objects are by default value types (their semantics depend on how you use them).T&) or pointer (T*) — these are not objects themselves but aliases or addresses referring to other objects.Key points:
- Declaring an object MyType x; instantiates a value object. If you use MyType* p = new MyType(); you allocate an object on the heap and p stores its address.
- struct vs class in C++ differ only by default access (public vs private).
Example:
struct Point { int x; int y; };
void example() {
Point a{1,2}; // value: object `a` exists directly
Point b = a; // copy constructed (value copy)
Point* ph = new Point{3,4}; // heap allocation
delete ph; // must free
}
Notes/Tips: - Value semantics by default makes copying cheap for small objects but expensive for large ones. - Use pointers/references for polymorphism and dynamic lifetime control.
Where objects live depends on how they are created:
new inside a function are typically placed on the stack (or may be optimized into registers). Stack allocation is fast and automatically freed when the function returns.new/malloc allocate memory on the heap. Heap allocations are relatively slow and the programmer (or RAII wrappers) must free them.Windows/MSVC specifics: - MSVC uses the OS virtual memory APIs for heap allocation; default CRT heap allows fragmentation handling, low-fragmentation heap options exist for production (LFH). - Stack size is configurable per-thread (default ~1MB for Windows threads created by the runtime). Watch recursion and large stack arrays.
Example:
void foo() {
int stackArray[1024]; // on the stack
int* heapArray = new int[1024]; // on the heap
// ...
delete[] heapArray; // free heap memory
}
Pitfalls:
- Stack overflow if you allocate huge local arrays or have deep recursion.
- Memory leaks if delete not called or exceptions bypass cleanup. Use RAII and smart pointers.
C++ copy semantics are controlled by copy constructor and assignment operator. By default the compiler-generated copy is a member-wise copy (shallow copy of fields).
std::move and move constructors/operators enable ownership transfer without expensive deep copies.Example (shallow vs deep):
struct Buffer {
size_t size;
char* data;
// default shallow copy (compiler-generated)
// custom deep copy
Buffer(const Buffer& other) : size(other.size) {
data = new char[size];
std::memcpy(data, other.data, size);
}
// move constructor (steal ownership)
Buffer(Buffer&& other) noexcept : size(other.size), data(other.data) {
other.data = nullptr; other.size = 0;
}
~Buffer() { delete[] data; }
};
Best practices:
- Prefer RAII containers (std::vector, std::string) which implement deep/move semantics correctly.
- Implement move operations for expensive-to-copy resources.
- Rule of five / zero: if you implement destructor/copy/move, follow the rule of five (or prefer no manual resource management and rely on standard containers).
nullptr (C++11+). Raw pointers can be nullptr.std::unique_ptr<T> for sole ownership; cannot be null-checked for lifetime but can hold nullptr.std::shared_ptr<T> for shared ownership with ref-counting.std::weak_ptr<T> to break cycles.std::optional<T> represents optional values without heap allocation for inline small types (C++17+).Example:
std::unique_ptr<Foo> p = std::make_unique<Foo>();
if (p) p->Do(); // check for non-null
std::optional<int> maybe;
maybe = 42;
if (maybe.has_value()) { int v = *maybe; }
Pitfalls:
- Do not mix delete and delete[] incorrectly.
- Avoid raw owning pointers; prefer unique_ptr/shared_ptr.
- Beware of shared_ptr cycles causing leaks — use weak_ptr to break cycles.
main.cpp:
#include <iostream>
#include <memory>
#include <optional>
#include <vector>
struct Node { int val; std::unique_ptr<Node> next; };
int main() {
// Stack vs Heap
int stackValue = 10; // stack
auto heapValue = std::make_unique<int>(20); // heap via unique_ptr
std::cout << "stack: " << stackValue << " heap: " << *heapValue << "\n";
// Deep vs shallow through vectors
std::vector<int> a = {1,2,3};
auto b = a; // deep copy (vector owns its memory) but shallow for elements that are pointers
// Move
auto c = std::move(a); // a is now empty, c has data without copying
// Optional
std::optional<int> maybe;
maybe = 100;
if (maybe) std::cout << "maybe: " << *maybe << "\n";
return 0;
}
Compile & run with MSVC as described earlier.
/F linker option to change stack size if needed./GS, buffer security mitigations and enable /RTC for runtime checks in debug./MD or /MT consistently for runtime library selection (DLL vs static CRT).End of C++ guide.