How is memory allocated/deallocated in C++?
In C++, memory allocation & deallocation for dynamic variables are done using the "new" & "delete" keywords, respectively.
To allocate memory dynamically, you use the "new" keyword followed by the data type you want to allocate memory for. For example, to allocate memory for an integer variable dynamically, you would write:
int* ptr = new int;
Here, "ptr" is a pointer variable that stores the address of the newly allocated memory. The "new" keyword returns the address of the allocated memory, which is then assigned to the pointer.
You can also allocate memory for arrays dynamically using "new". To allocate memory for an array of integers, you would write:
int* arr = new int[size];
In this case, "size" is a variable that specifies the number of elements in the array. The "new" keyword allocates a block of memory that can accommodate "size" integers & returns the address of the first element.
When you no longer need the dynamically allocated memory, it's important to deallocate it using the "delete" keyword. To deallocate memory pointed to by a single pointer, you use:
delete ptr;
To deallocate memory allocated for an array, you use the "delete[]" syntax:
delete[] arr;
It's very important to use the correct form of "delete" based on whether you allocated memory for a single variable or an array. Using the wrong form can lead to undefined behavior.
Syntax
The syntax for using the "new" keyword in C++ is very easy. Let’s see the syntax for allocating memory for a single variable:
datatype* pointer_name = new datatype;
- `datatype`: The data type of the variable you want to allocate memory for (e.g., int, float, double).
- `pointer_name`: The name of the pointer variable that will store the address of the allocated memory.
For example, to allocate memory for a single integer variable:
int* ptr = new int;
When allocating memory for an array, the syntax is slightly different:
datatype* pointer_name = new datatype[size];
- `datatype`: The data type of the elements in the array.
- `pointer_name`: The name of the pointer variable that will store the address of the allocated memory.
- `size`: The number of elements in the array.
For example, to allocate memory for an array of 10 integers:
int* arr = new int[10];
To deallocate the memory, you use the "delete" keyword followed by the pointer variable:
delete pointer_name; // For a single variable
delete[] pointer_name; // For an array
It's important to note that you should only deallocate memory that was previously allocated using "new". Attempting to deallocate memory that was not dynamically allocated or deallocating the same memory twice can lead to undefined behavior.
Parameters
When we are using the "new" keyword in C++, we can specify parameters to initialize the dynamically allocated variable or array.
Let’s discuss the different ways to use parameters with "new":
1. Allocating a single variable with default initialization
datatype* pointer_name = new datatype;
In this case, the allocated variable is default-initialized. For example, if you allocate an integer using `int* ptr = new int;`, the integer will be default-initialized to 0.
2. Allocating a single variable with a specified value
datatype* pointer_name = new datatype(value);
Here, you can provide an initial value for the allocated variable. For example, `int* ptr = new int(42);` allocates an integer & initializes it with the value 42.
3. Allocating an array with default initialization
datatype* pointer_name = new datatype[size];
This syntax allocates an array of the specified size, & each element of the array is default-initialized. For example, `int* arr = new int[5];` allocates an array of 5 integers, & each integer is default-initialized to 0.
4. Allocating an array with a specified initial value
datatype* pointer_name = new datatype[size] {value1, value2, ...};
In this case, you can provide a list of initial values for the elements of the array. The number of values provided should not exceed the specified size of the array. For example, `int* arr = new int[3] {1, 2, 3};` allocates an array of 3 integers & initializes them with the values 1, 2, & 3.
It's important to note that when you allocate memory dynamically using "new," you are responsible for deallocating that memory using "delete" when it is no longer needed. If you don't deallocate the memory it can result in memory leaks.
Return Value
Whenever we use the "new" keyword in C++, it returns a pointer to the newly allocated memory. The return value of "new" is of the same data type as the pointer you are assigning it to.
Let’s see a few important points about the return value of "new":
1. Pointer to the allocated memory
The "new" keyword returns a pointer that points to the beginning of the allocated memory block. This pointer can be assigned to a pointer variable of the appropriate data type. For example:
int* ptr = new int;
In this case, "new int" allocates memory for an integer & returns a pointer to that memory, which is then assigned to the pointer variable "ptr".
2. nullptr on failure
If the memory allocation fails due to insufficient memory or other reasons, "new" returns a special value called "nullptr". It is a good practice to check the return value of "new" against nullptr to handle allocation failures gracefully. For example:
int* ptr = new int;
if (ptr == nullptr) {
// Handle memory allocation failure
// ...
}
By checking if the pointer is nullptr, you can take appropriate action, like displaying an error message or terminating the program.
3. Type of the returned pointer
The type of the pointer returned by "new" matches the data type you specified. For example, if you allocate memory for an integer using `new int`, the returned pointer will be of type `int*`. Similarly, if you allocate memory for a floating-point number using `new double`, the returned pointer will be of type `double*`.
It's important to store the returned pointer in a variable of the appropriate type to avoid type mismatches & to ensure proper memory management.
Normal Array Declaration vs Using new
In C++, you can declare arrays in two ways: using normal array declaration syntax or using the "new" keyword for dynamic memory allocation. Let's compare these two approaches:
1. Normal Array Declaration
int arr[5];
- The size of the array is fixed at compile time & cannot be changed during runtime.
- The memory for the array is allocated on the stack.
- The memory is automatically deallocated when the array goes out of scope.
- The size of the array must be known at compile time.
2. Using "new" for Dynamic Array Allocation
int* arr = new int[5];
- The size of the array can be determined at runtime & can be changed dynamically.
- The memory for the array is allocated on the heap.
- The memory remains allocated until you explicitly deallocate it using the "delete[]" keyword.
- The size of the array can be specified using a variable or an expression.
Let’s discuss some of the key differences between normal array declaration & using "new":
1. Memory Allocation:
- Normal array declaration allocates memory on the stack, which is limited in size.
- "new" allocates memory on the heap, which is larger & can accommodate dynamic memory requirements.
2. Array Size:
- With normal array declaration, the size of the array is fixed at compile time & cannot be changed during runtime.
- Using "new," the size of the array can be determined dynamically at runtime, allowing for flexibility.
3. Scope & Lifetime:
- Arrays declared using normal syntax have automatic storage duration & are destroyed when they go out of scope.
- Arrays allocated using "new" have dynamic storage duration & persist until explicitly deallocated using "delete[]".
4. Syntax:
- Normal array declaration uses square brackets `[]` to specify the size of the array.
- Dynamic allocation with "new" uses the "new" keyword followed by the data type & square brackets `[]` to specify the size.
Let’s see an example showing the difference clearly :
// Normal array declaration
int normalArr[5] = {1, 2, 3, 4, 5};
// Dynamic array allocation using "new"
int size = 5;
int* dynamicArr = new int[size];
for (int i = 0; i < size; i++) {
dynamicArr[i] = i + 1;
}
// Deallocate the dynamically allocated array
delete[] dynamicArr;
In the above example, `normalArr` is declared using the normal array declaration syntax with a fixed size of 5, while `dynamicArr` is allocated dynamically using "new" with a size determined by the `size` variable.
What if enough memory is not available during runtime?
When you use the "new" keyword to allocate memory dynamically in C++, there is a possibility that the memory allocation may fail if there is not enough memory available during runtime. In such cases, C++ provides mechanisms to handle the situation. Let's discuss what happens & how you can deal with insufficient memory during runtime.
1. std::bad_alloc exception
By default, when memory allocation fails using "new," C++ throws an exception of type `std::bad_alloc`. This exception is defined in the `<new>` header & indicates that the memory allocation request could not be fulfilled. If you don't catch & handle this exception, it will propagate up the call stack & eventually terminate the program if not caught.
Example of how you can catch & handle the `std::bad_alloc` exception:
try {
int* ptr = new int[1000000000]; // Attempt to allocate a large amount of memory
// ...
} catch (const std::bad_alloc& e) {
std::cout << "Memory allocation failed: " << e.what() << std::endl;
// Handle the exception, e.g., display an error message or take alternative action
}
In this example, if the memory allocation fails, the `std::bad_alloc` exception will be thrown & caught by the `catch` block. You can then handle the exception appropriately, such as displaying an error message or taking alternative actions.
2. Checking the return value of "new"
Another way to handle memory allocation failures is to check the return value of "new" against nullptr. If "new" fails to allocate memory, it returns a nullptr. You can use this to detect allocation failures & take appropriate action.
For example :
int* ptr = new (std::nothrow) int[1000000000]; // Attempt to allocate a large amount of memory
if (ptr == nullptr) {
std::cout << "Memory allocation failed." << std::endl;
// Handle the failure, e.g., display an error message or take alternative action
} else {
// Memory allocation succeeded, use the allocated memory
// ...
delete[] ptr; // Remember to deallocate the memory when done
}
In this case, we use the `std::nothrow` parameter with "new" to indicate that we don't want an exception to be thrown if the allocation fails. Instead, "new" will return a nullptr. We then check if `ptr` is nullptr & handle the failure accordingly.
It's important to note that in both approaches, you should handle memory allocation failures very diligently. Depending on your program's requirements, you may choose to display an error message, log the failure, or take alternative actions like using a fallback mechanism or terminating the program.
Frequently Asked Questions
Can I use "new" to allocate memory for objects?
Yes, you can use "new" to allocate memory for objects. Simply use the class name instead of a primitive data type.
Is it necessary to deallocate memory allocated with "new"?
Yes, it is crucial to deallocate memory allocated with "new" using the "delete" keyword to avoid memory leaks.
Can I allocate memory dynamically for multidimensional arrays using "new"?
Yes, you can allocate memory for multidimensional arrays using "new". You need to use multiple sets of square brackets to specify the dimensions.
Conclusion
In this article, we have learned about the "new" keyword in C++ & how it allows us to allocate memory dynamically during runtime. We discussed the differences between memory allocation for normal variables & dynamically allocated memory using "new". We also covered the syntax & parameters for using "new" & understood the return value it provides. Moreover, we compared normal array declaration with dynamic array allocation using "new". Lastly, we discussed how to handle situations when enough memory is not available during runtime by catching exceptions or checking the return value of "new"
You can also check out our other blogs on Code360.