Memory Management in C++
Read at your own pace. Code execution, quizzes, progress tracking, and the AI tutor are available in the interactive view with JavaScript enabled.
Why Memory Matters
From using objects to owning resources
In Introduction to C++, you compiled programs, used variables, and organized logic with functions. In Object-Oriented Programming in C++, you built classes that protect invariants. In Working with Files in C++, you used streams to interact with resources outside your program.
Now we zoom in on one of C++'s biggest differences from JavaScript: memory is not just something the runtime hides from you.
In JS, objects live until the garbage collector decides they are unreachable. In C++, you can often choose:
- where an object lives
- when its lifetime begins
- when its lifetime ends
- who is responsible for cleanup
This power is why C++ is used in game engines, embedded electronics, audio tools, browsers, and high-performance systems. But it also means you need a clear ownership model.

Stack And Heap
Two common memory regions
A useful mental model is that C++ objects often live in either the stack or the heap.
Stack
The stack is for local variables with automatic lifetime.
function starts -> local variables created
function returns -> local variables destroyed
Stack allocation is fast and predictable. If you declare an int score = 23; inside a function, it usually lives on the stack.
Heap
The heap is for dynamic allocation: memory requested while the program runs.
ask for memory -> use it -> release it later
The heap is useful when an object's lifetime must outlive the current function, when size is not known until runtime, or when ownership must move between parts of a program.
Think of a home renovation: the stack is like tools on your belt during one task; the heap is like materials stored in the garage that may be used across multiple tasks.

Pointers Hold Addresses
A pointer is not the object
A pointer stores a memory address. It can point to an object, but it is not the object itself.
That distinction matters because C++ lets you pass around addresses directly. This can be powerful, like wiring components on a breadboard in electronics: a wire is not the LED, but it can connect you to the LED.
Key operators:
-
&xmeans "address ofx" -
int* pmeans "pis a pointer to anint" -
*pmeans "the value at the address stored inp"
```cpp
#include <iostream>
int main() {
int points = 21;
int* ptr = &points;
std::cout << "points value: " << points << "\n";
std::cout << "ptr points to value: " << *ptr << "\n";
*ptr = 24;
std::cout << "after update through pointer: " << points << "\n";
return 0;
}
```
Raw Dynamic Allocation
new creates, delete destroys
C++ supports raw dynamic allocation with new and delete.
new -> request heap memory and construct an object
delete -> destroy the object and release the memory
This is the low-level version of memory management. You should understand it because it explains what smart pointers automate, but modern C++ code usually avoids owning raw new directly.
A raw pointer created with new creates a responsibility: someone must call delete exactly once.
```cpp
#include <iostream>
int main() {
int* score = new int(98);
std::cout << "Heap score: " << *score << "\n";
delete score;
score = nullptr;
if (score == nullptr) {
std::cout << "score no longer points to an int\n";
}
return 0;
}
```
Who Owns It?
A function creates an object with new and returns the raw pointer to its caller.
What is the most important question the caller must be able to answer?
Leaks And Dangling
Two classic memory bugs
Raw pointers can fail in several ways. Two of the most important are memory leaks and dangling pointers.
Memory leak
A leak happens when allocated memory is no longer reachable or never released.
allocate -> lose pointer -> cannot delete
Your program may keep running, but it slowly consumes more memory. Like leaving lights on in every room during a renovation, each one seems small until the whole house is wasting power.
Dangling pointer
A dangling pointer points to memory that has already been destroyed.
allocate -> delete -> still use old pointer
This is especially dangerous because the program might appear to work sometimes and crash later. In C++, "it ran once" is not proof that memory usage is safe.

Delete Means Safe?
True or false:
After calling delete on a raw pointer, it is safe to keep dereferencing that same pointer as long as the program has not crashed yet.
RAII And Smart Pointers
Prefer lifetime-bound cleanup
Modern C++ leans on a pattern called RAII: Resource Acquisition Is Initialization.
The idea:
object constructed -> resource acquired
object destroyed -> resource released
You already saw this mindset in Working with Files in C++: file stream objects clean up when they go out of scope.
Smart pointers apply the same idea to heap memory.
std::unique_ptr
Use std::unique_ptr when exactly one owner controls an object. When the unique_ptr is destroyed, it automatically deletes the object.
This is usually your first choice for heap ownership.
```cpp
#include <iostream>
#include <memory>
#include <string>
class Track {
public:
Track(std::string title) : title(title) {
std::cout << "Loaded track: " << this->title << "\n";
}
~Track() {
std::cout << "Released track: " << title << "\n";
}
void play() const {
std::cout << "Playing: " << title << "\n";
}
private:
std::string title;
};
int main() {
auto track = std::make_unique<Track>("Practice Beat");
track->play();
std::cout << "Leaving scope...\n";
return 0;
}
```
Create Unique Pointer
Fill in the blank with the standard helper used to create a std::unique_ptr safely:
auto item = ______<Item>();
Memory Habits
Practical rules for modern C++
As a web developer, think of C++ memory management as moving from "the runtime eventually cleans it up" to "the code expresses ownership clearly."
Use these habits:
- Prefer regular local objects when possible.
- Prefer standard containers like
std::vectorandstd::stringfor dynamic data. - Prefer
std::unique_ptrfor single ownership. - Use
std::shared_ptronly when shared ownership is part of the design. - Avoid raw owning pointers in application code.
- If you use
new, know exactly where the matchingdeletehappens. - After deleting a raw pointer, do not use it; setting it to
nullptrcan reduce mistakes.
The goal is not to memorize every memory API. The goal is to make object lifetimes obvious. In the next lesson, Error Handling in C++, we will look at what happens when operations fail and how to keep programs reliable.

