Lesson 24
Dynamic Memory (new/delete)
Covers new and delete syntax, stack vs heap memory, dynamic arrays, memory leaks, dangling pointers, and dynamic 2D arrays.
Lesson content
What is dynamic memory?
Dynamic memory is memory that is allocated during program execution (runtime) using the new operator and released using the delete operator. Unlike local variables, dynamic memory is stored in the heap instead of the stack.
- The stack is like a hotel room, automatically cleaned up when you leave
- The heap is like renting a storage unit, you decide when to rent it and when to give it back
Why does dynamic memory exist?
- Storing user-defined amounts of data
- Creating dynamic arrays
- Building linked lists, trees, and graphs
- Managing objects that need to outlive a function call
Real-world use cases
- Dynamic arrays
- Linked lists
- Binary trees
- Graphs
- Game objects
- Database records
- File processing
- Large datasets
Prerequisites
- Variables
- Data types
- Functions
- Pointers
- References
- Memory basics (stack vs heap)
Stack memory
Variables declared normally are stored on the stack.
int number = 10;
// Stack
// +------------+
// | number=10 |
// +------------+The memory is released automatically when the variable goes out of scope.
Heap memory
Heap memory is allocated manually.
int* ptr = new int;
// Stack Heap
//
// ptr ---------------> +--------+
// | ? |
// +--------+The pointer lives on the stack, while the allocated integer lives on the heap.
Using new
pointer = new dataType;
int* ptr = new int;
// With initialization:
int* ptr2 = new int(100);
// Stack Heap
//
// ptr2 ------------------> +------+
// | 100 |
// +------+Using delete
Memory allocated with new must be released using delete.
delete ptr;
// After deletion:
// Stack Heap
//
// ptr ----X Memory Freed
// To avoid accidental use of the old address:
ptr = nullptr;Dynamic arrays
Allocate an array at runtime.
int size = 5;
int* arr = new int[size];
// Stack Heap
//
// arr ------------------> +----+----+----+----+----+
// | | | | | |
// +----+----+----+----+----+
// Release it using:
delete[] arr;Memory leak
A memory leak happens when allocated memory is never released.
int* ptr = new int(50);
// Forgot delete
// Heap
// +------+
// | 50 |
// +------+
// (No pointer can free it later)Always pair new with delete.
Dangling pointer
A dangling pointer points to memory that has already been freed.
int* ptr = new int(10);
delete ptr;
// ptr
// |
// V
// Invalid Memory
// Fix:
delete ptr;
ptr = nullptr;Example 1: Allocating a single integer
#include <iostream>
using namespace std;
int main()
{
int* ptr = new int(25);
cout << *ptr << endl;
delete ptr;
ptr = nullptr;
return 0;
}Example 2: Dynamic array
#include <iostream>
using namespace std;
int main()
{
int size = 5;
int* arr = new int[size];
for (int i = 0; i < size; i++)
{
arr[i] = (i + 1) * 10;
}
for (int i = 0; i < size; i++)
{
cout << arr[i] << " ";
}
delete[] arr;
arr = nullptr;
return 0;
}
// Output: 10 20 30 40 50Example 3: Dynamic object
#include <iostream>
#include <string>
using namespace std;
struct Student
{
string name;
int age;
};
int main()
{
Student* student = new Student;
student->name = "Alice";
student->age = 20;
cout << student->name << endl;
cout << student->age << endl;
delete student;
student = nullptr;
return 0;
}Example 4: Dynamic 2D array
#include <iostream>
using namespace std;
int main()
{
int rows = 2;
int cols = 3;
int** matrix = new int*[rows];
for (int i = 0; i < rows; i++)
{
matrix[i] = new int[cols];
}
int value = 1;
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
matrix[i][j] = value++;
}
}
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
cout << matrix[i][j] << " ";
}
cout << endl;
}
for (int i = 0; i < rows; i++)
{
delete[] matrix[i];
}
delete[] matrix;
matrix = nullptr;
return 0;
}
// Output:
// 1 2 3
// 4 5 6Example 5: Runtime-sized array
#include <iostream>
using namespace std;
int main()
{
int size;
cout << "Enter array size: ";
cin >> size;
int* numbers = new int[size];
for (int i = 0; i < size; i++)
{
numbers[i] = i + 1;
}
for (int i = 0; i < size; i++)
{
cout << numbers[i] << " ";
}
delete[] numbers;
numbers = nullptr;
return 0;
}Common mistakes and pitfalls
- Forgetting delete after new (int* p = new int;). Fix: delete p; p = nullptr;
- Using delete[] for memory allocated with new. Fix: use delete for a single object
- Using delete for memory allocated with new[]. Fix: use delete[] for arrays
- Using *ptr after delete ptr;. Fix: set ptr = nullptr; and don't dereference it
- Calling delete twice on the same pointer. Fix: delete once, then set the pointer to nullptr
Best practices
- Prefer smart pointers (unique_ptr, shared_ptr) over raw new and delete
- Pair every new with exactly one matching delete
- Pair every new[] with exactly one matching delete[]
- Initialize pointers to nullptr when they don't yet own memory
- Set pointers to nullptr after deleting them if they may be used again
- Minimize manual memory management by using standard library containers such as std::vector whenever possible
When not to use this
- A local variable on the stack is sufficient
- You can use std::vector instead of a dynamic array
- You can use std::string instead of manually allocated character arrays
- You need automatic resource management, prefer smart pointers and RAII
- You're writing modern C++ code where standard containers already solve the problem
Summary
- Dynamic memory is allocated at runtime using new
- Dynamically allocated objects are stored on the heap
- Use delete to free a single object allocated with new
- Use delete[] to free arrays allocated with new[]
- Forgetting to release memory causes memory leaks
- Accessing freed memory results in dangling pointers and undefined behavior
- Set pointers to nullptr after deletion when appropriate
- In modern C++, prefer std::vector and smart pointers over manual memory management
Frequently asked questions
What is the difference between stack and heap memory?
The stack stores local variables and automatically releases them when they go out of scope. The heap stores dynamically allocated memory, which must be released manually (or automatically through smart pointers).
Why do I need delete after new?
Memory allocated with new remains allocated until you free it. If you don't call delete, the program leaks memory.
What's the difference between delete and delete[]?
Use delete for a single object allocated with new. Use delete[] for an array allocated with new[]. Using the wrong form results in undefined behavior.
What is a memory leak?
A memory leak occurs when allocated heap memory is never released, making it unavailable for the rest of the program.
Should I use new and delete in modern C++?
Usually no. Modern C++ encourages using RAII, smart pointers (unique_ptr, shared_ptr), and standard containers like std::vector, which manage memory automatically and reduce the risk of leaks and dangling pointers.