Memory Allocation
In this lesson, we will learn about an important subsection of memory management, i.e., memory allocation.
We'll cover the following
Introduction
Explicit memory management in C++ has a high complexity but also provides us with great functionality. Sadly, this special domain of C++ is not so well known.
For example, we can directly create objects in static memory, in a reserved area, or even in a memory pool. This functionality is often key in safety-critical applications in the embedded world.
-
C++ enables the dynamic allocation and deallocation of memory.
-
Dynamic memory, or the heap, has to be explicitly requested and released by the programmer.
-
We can use the operators
new
andnew[]
to allocate memory and the operatorsdelete
anddelete[]
to deallocate memory. -
The compiler manages its memory automatically on the stack.
Smart pointers manage memory automatically.
Memory allocation
new
Thanks to the new
operator, we can dynamically allocate memory for the instance of a type.
int* i = new int;
double* d = new double(10.0);
Point* p = new Point(1.0, 2.0);
-
new
causes memory allocation and object initialization. -
The arguments in the brackets go directly to the constructor.
-
new
returns a pointer to the corresponding object. -
If the class of dynamically created objects is part of a type hierarchy, more constructors are invoked.
new[]
new[]
allows us to allocate memory to a C array. The newly created objects need a default constructor.
double* d = new double[5];
Point* p = new Point[10];
-
The class of the allocated object must have a default constructor.
-
The default constructor will be invoked for each element of the C array.
The STL Containers and the C++ String automatically manage their memory.
Placement new
Placement new
is often used to instantiate an object or a C array in a specific area of memory. In addition, we can overload placement new
globally or for our own data types. This is a big benefit offered by C++.
char* memory = new char[sizeof(Account)]; // allocate std::size_t
Account* acc = new(memory) Account; // instantiate acc in memory
-
The header,
<new>
, is necessary. -
Can be overloaded on a class basis or globally.
Typical use-cases
-
Explicit memory allocation
-
Avoidance of exceptions
-
Debugging
Failed allocation
If the memory allocation operation fails, new
and new[]
will raise a std::bad_alloc
exception. But that is not the behavior we want. Therefore, we can invoke placement new
with the constant std::nothrow
. This call will return a nullptr
in the case of failure.
char* c = new(std::nothrow) char[10];
if (c){
delete c;
}
else{
// an error occured
}
New handler
In the case of a failed allocation, we can use std::set_new_handler
with our own handler. std::set_new_handler
returns the older handler and needs a callable unit. A callable unit is typically a function, a function object, or a lambda-function. The callable unit should take no arguments and return nothing. We can get the handler currently being used by invoking the function std::get_new_handler
.
Our own handler allows us to implement special strategies for failed allocations:
- request more memory
- terminate the program with
std::terminate
- throw an exception of type
std::bad_alloc
Further information
In the next lesson, we will study how to deallocate memory.
Get hands-on with 1200+ tech skills courses.