Hi there! 👋
Somebody once said that the perfect way to spend Sunday is by doing a recap of what you learned in BitesizedEngineering series on C++ Memory Allocation :)
As usual, the bitesized graphic first and then more details for those eager to understand more details:
I hope that was useful :)
So, in a nutshell, what we have is:
malloc() — which I’d guess stands for “Memory Allocation” is a C function for allocating the memory. Given that C++ was meant as an extension of C (hence the “++” meaning an increment of C), malloc() exists in C++ but people generally recommend avoiding it. Here’s a nice StackOverflow thread if you want details.
“new” expression — which I occasionally call “keyword new” for reasons known to myself only, is basically a C++ way for allocating memory. I’ll discuss more details about it below.
HeapAlloc() — is part of Windows’ API and as such should be used ONLY if you’re targeting Windows only. For any other case you should really stick to “new”.
VirtualAlloc() — is also part of Windows’ API and represents the lowest-level API that you can interact with. It enables some amazing stuff, but also comes with a cost. We’ll discuss it below as well.
void* memory = malloc(1024);
char* memory = new char[1024];
void* memory = HeapAlloc(GetCurrentProcess(),
HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY, 1024);
// You get back 4096 bytes (i.e. 1 page)
void* memory = VirtualAlloc(nullptr, 1024, MEM_COMMIT, PAGE_READWRITE);
As you can see above, “new” is the only one that works with actual Data Types. All others work with raw bytes. What’s more, HeapAlloc() and VirtualAlloc() require some additional properties, which, frankly, are the exact reason why you’d likely be using them in the first place - to tweak the memory allocation behavior :)
Now let’s answer the “so, what should I use?” question. And the common answer seems to be - unless you know WHAT and WHY you are doing it, you should likely stick to expression “new”.
One of the main benefits of the “new” vs everything else is that it guarantees type-safety. As in - with “new” you ensure that you get only as much bytes as you need for DATA TYPE that you want to create (remember when we discussed that C++ is all about Data Types?). On the other hand, with malloc(), HeapAlloc() and VirtualAlloc() you get back a void*
— which is a confusing way to inform you that you get a pointer, pointing to some memory address, but it’s up to you to figure out how big that SOMETHING is (hence the “void” part).
Another “issue” with malloc(), HeapAlloc() and VirtualAlloc() is that you get back a NULL if there’s no free memory to allocate. “new” will generate exception, which is a preferred OOP way to handle exceptional scenarios. You can override this behavior with Heap and VirtualAlloc(), but still, it’s something to keep in mind.
Yet another thing to keep in mind is that “new” will invoke a Constructor (and Destructor), whereas other APIs won’t do that. I guess this communicates that “new” is much more than a Memory Allocator, right? It’s really ALL what C++ is about - Data Types. And this aligns really nicely for those coming from pretty much any managed language, where you think of “new” as a way to construct an object :)
Finally, one cool question that one might ask is — so, if “new” is all about Data Types, and specifying data types, how do you allocate large chunks of memory then? And the answer is quite simple - you can use the operator new and allocate bigger chunks internally (by invoking malloc() / HeapAlloc() / VirtualAlloc() under the hood).
Fun fact btw: in Windows, “new” actually uses malloc() under the hood, and malloc() calls HeapAlloc() which in turn calls VirtualAlloc(). You can read more about it here.
And that’d be about it for today :) Next week, as I announced before, I’m going to start sharing some insights on how CLR works (i.e. a .NET’s Runtime Engine), but in parallel I’ll be working on some articles on how memory paging works, and what are User and Kernel modes in Windows (and how do they affect the memory). Until then, if you liked this, I’d appreciate if you share it! :)
Thanks for reading!
Other articles from the C++ Memory Management series: