I've seen many posts about memory allocation in C++, questions about "new operator" vs "operator new", questions about new int(100)
vs new int[100]
, questions about memory initialization... I think there should be an answer that summarizes everything clearly once and for all, and I'm choosing this question to write this summary. It is about dynamic memory allocation, ie allocations on the heap at runtime. I also provide a summary implementation (public domain).
C vs C++
Main functions for dynamic memory allocations:
- In C (header
<cstdlib>
), we have mainly malloc
and calloc
and free
. I won't talk about realloc
.
- in C++ (header
<new>
), we have:
- Template single-object allocation with initialization arguments:
new T( args )
new (std::nothrow) T( args )
delete ( T* )
- Template multiple-objects allocation with default initialization:
new T[ size_t ]
new (std::nothrow) T[ size_t ]
delete[] ( T* )
- Template memory initialization without allocation for single or multiple objects:
new (void*) T( args )
new (void*) T[ size_t ]
- Internal new-expressions for:
- Raw memory allocation
::operator new( size_t )
;
- Raw memory allocation without exception
::operator new( size_t, std::nothrow )
;
- Raw memory initialization without allocation
::operator new( size_t, ptr )
.
Please look at this post for a concise comparison.
Legacy C dynamic allocations
Main points: complete type-erasure (void*
pointers), and therefore no construction/destruction, size specified in bytes (typically using sizeof
).
malloc( size_t )
does not initialize memory at all (raw memory contains garbage, always initialize manually before use). calloc( size_t, size_t )
initializes all bits to 0 (slight overhead, but useful for POD numeric types). Any allocated memory should be released using free
ONLY.
Construction/destruction of class instances should be done manually before use / before memory release.
C++ dynamic allocations
Main points: confusing because of similar syntaxes doing different things, all delete
-statements call the destructor, all delete
-statements take fully typed pointers, some new
-statements return fully-typed pointers, some new
-statements call some constructor.
Warning: as you will see below, new
can either be a keyword OR function. It is best not to talk about "new operator" and/or "operator new" in order to avoid confusions. I call "new
-statements" any valid statements that contain new
either as a function or keyword. People also talk about "new
-expressions", where new
is the keyword and not the function.
Raw memory allocation (no initialization)
Do not use this yourself. This is used internally by new-expressions (see below).
These allocations do not initialize memory, and in particular, they do not call the default-constructor on the allocated objects. Therefore you MUST initialize ALL the elements manually before you release the allocation using either delete
or delete[]
.
Note: I couldn't stress enough that you should NOT use this yourself. If you should use it, however, make sure you pass a pointer to void
instead of a typed pointer when calling either delete
or delete[]
on such allocations (always after initializing manually). I have personally experienced runtime errors with non-POD types with some compilers (maybe my mistake).
Raw memory initialization (no allocation)
Do not use this yourself. This is used internally by new-expressions (see below).
In the following, I assume void *ptr = ::operator new( n*sizeof(T) )
for some type T
and size n
.
Then ::operator new( n*sizeof(T), (T*) ptr )
initializes n
elements of type T
starting from ptr
using the default constructor T::T()
. There is no allocation here, only initialization using the default-constructor.
Single-object allocation & initialization
new T( args )
allocates and initializes memory for a single object of type T
using the constructor T::T( args )
. The default constructor will not be called unless arguments are omitted (ie new T()
or even new T
). Throws an exception std::bad_alloc
on failure.
- Same for
new (std::nothrow) T( args )
except that it returns NULL
in case of failure.
- Use
delete
to call the destructor T::~T()
and release the corresponding memory.
Multiple-objects allocation & initialization
new T[n]
allocates and initializes memory for a n
objects of type T
using the default constructor. Throws an exception std::bad_alloc
on failure.
- Idem for
new (std::nothrow) T[n]
except that it returns NULL
in case of failure.
- Use
delete[]
to call the destructor T::~T()
for each element and release the corresponding memory.
Memory initialization (aka "placement new")
No allocation here. Regardless of how the allocation was made:
new (ptr) T(args)
calls the constructor T::T(args)
on the memory stored at ptr
. The default constructor is not called unless arguments are omitted.
new (ptr) T[n]
calls the default constructor T::T()
on n
objects of type T
stored from ptr
to ptr+n
(ie, n*sizeof(T)
bytes).
Related posts