To manage dynamic memory, C++ provides two operators new
and delete
to allocate and deallocate memory, respectively.
In C++, memory can be managed in two main ways:
Automatic memory: It is automatically allocated when a local or global variable is declared and is automatically freed when the variable goes out of scope.
Dynamic memory: It is manually allocated and deallocated by the programmer during the execution of the program using the
new
anddelete
operators.
Basically, dynamic memory is storage space that we need, but we do not know how much we need until runtime (for example, because it depends on a system input).
Unlike automatic memory, memory allocated with new
persists until it is explicitly freed with delete
.
What is new
The new
operator in C++ is used to allocate dynamic memory (i.e., to tell the system “hey!, give me a place to store this”).
data_type* pointer = new data_type;
- data_type: This is the data type for which memory is being allocated.
- pointer: This is a pointer that will store the address of the allocated memory.
Let’s see it with an example,
int* p = new int; // Allocates memory for an integer
*p = 5; // Stores the value 5 in the allocated memory
std::cout << *p << std::endl; // Prints 5
In this example,
- Memory is allocated for an integer
- The pointer
p
points to that memory - The value
5
is stored in that memory location. - Through it, we can access the stored value.
Dynamic Array Allocation
The new
operator can also be used to allocate memory for dynamic arrays. The syntax is slightly different:
data_type* pointer = new data_type[size];
That is, it is basically the same, but we put []
indicating the size of the variable to be allocated. Let’s see it with an example,
int* arr = new int[5]; // Allocates memory for an array of 5 integers
for(int i = 0; i < 5; ++i) {
arr[i] = i * 2; // Initializes the array with values
}
for(int i = 0; i < 5; ++i) {
std::cout << arr[i] << " "; // Prints the values of the array
}
std::cout << std::endl;
This example shows how to allocate and use a dynamic array. Here, arr
is a pointer to a block of memory that can hold 5 integers.
What is delete
The delete
operator is used to free the memory that was dynamically allocated with new
.
delete pointer;
- pointer: This is the pointer that points to the memory that should be freed.
Let’s see it with an example
int* p = new int; // Allocates memory for an integer
*p = 5; // Stores the value 5 in the allocated memory
delete p; // Frees the memory
p = nullptr; // Prevents the pointer from pointing to freed memory
In this example,
- After using the allocated memory, it is freed with
delete
nullptr
is assigned to the pointer to prevent it from pointing to an address that is no longer valid.
Freeing Dynamic Arrays
When memory is allocated for a dynamic array, it is important to free that memory correctly using the delete[]
operator.
delete[] pointer;
For example
int* arr = new int[5]; // Allocates memory for an array of 5 integers
for(int i = 0; i < 5; ++i) {
arr[i] = i * 2;
}
// Use of the array
delete[] arr; // Frees the memory of the array
arr = nullptr; // Prevents access to freed memory
In this example, the memory allocated for the array is freed using delete[]
.
It is important to use delete[]
instead of delete
to avoid undefined behavior (a.k.a something crashing)
Common Errors When Using new and delete
The most common error is forgetting to do a delete
when we no longer need a variable, or when we are going to reallocate it.
void memoryLeak() {
int* p = new int; // Allocates memory for an integer
// Forgetting to free the memory
p = new int; // Reallocating memory
}
In this case, the pointer p
is destroyed when leaving the function, but the allocated memory is not freed, causing a leak.
This is called a memory leak and it is a very common error.
This leads to the program consuming more and more memory and, eventually, crashing 💥 (and it’s also a mess as programmers).
The memory will be freed in any case when the program closes. Don’t think that it’s a permanent issue to the system.
Another common error is accessing memory that has already been freed with delete
, which will also cause an error in your program.
int* p = new int(10);
delete p; // Frees the memory
*p = 5; // Error: accessing freed memory
This example shows an access to memory after it has been freed, which can cause another crash in your program 💥.
Freeing the same memory more than once is another common error, known as double freeing.
void doubleFree() {
int* p = new int(10);
delete p;
delete p; // Error: double freeing
}
In this case, an attempt is made to free the same memory twice, which is a serious error.
Best Practices
Pairing new and delete
Every time new
is used to allocate memory, there should be a corresponding delete
somewhere to free that memory.
Use nullptr after delete
After freeing memory with delete
, it is good practice to assign nullptr
to the pointer. This prevents accidental access to freed memory.
delete p;
p = nullptr;
Use smart pointers
Instead of manually managing dynamic memory with new
and delete
, it is recommended to use smart pointers like std::unique_ptr
and std::shared_ptr
, which automatically manage memory and help prevent memory leaks and invalid accesses.
Don’t over-allocate memory
It is important to carefully allocate the necessary amount of memory (don’t allocate recklessly).
In the end, it is a limited resource, and like any resource, its use should be minimized if not necessary.