My question is not a duplicate of Is it safe to `free()` memory allocated by `new`?.
I'm writing a toy garbage collector for PODs, in which I'm defining my own custom operator new/new[]
and operator delete/delete[]
. Code below:
#include <iostream>
#include <map>
std::map<void*, std::size_t> memory; // globally allocated memory map
struct collect_t {} collect; // tag for placement new
void* operator new(std::size_t size, const collect_t&)
{
void* addr = malloc(size);
memory[addr] = size;
return addr;
}
void* operator new[](std::size_t size, const collect_t&)
{
return operator new(size, collect);
}
void operator delete(void *p, const collect_t&) noexcept
{
memory.erase(p); // should call ::operator delete, no recursion
free(p);
}
void operator delete[](void *p, const collect_t&) noexcept
{
operator delete(p, collect);
}
void display_memory()
{
std::cout << "Allocated heap memory: " << std::endl;
for (auto && elem : memory)
{
std::cout << "\tADDR: " << elem.first << " "
<< "SIZE: " << elem.second << std::endl;
}
}
void clear()
{
for (auto && elem : memory)
free(elem.first); // is this safe for arrays?
memory.clear();
}
int main()
{
// use the garbage collector
char *c = new(collect) char;
int *p = new(collect) int[1024]; // true size: sizeof(int)*1024 + y (unknown overhead)
display_memory();
clear();
display_memory();
}
The idea is simple: I store all allocated tracked addresses (the ones allocated with my custom new
) in a std::map
, and make sure that at the end of the day I clear all memory in my clear()
function. I use a tag for my new
and delete
(and don't overload the global ones) so that std::map
's allocator can call the global ones without recurring.
My question is the following: in my clear()
function, I de-allocate the memory in the line
for (auto && elem : memory)
free(elem.first); // is this safe for arrays?
Is this safe for arrays, e.g. for int *p = new(collect) int[1024];
?. I believe it is, since void* operator new[](std::size_t size, const collect_t&)
calls operator new(size, collect);
, and the latter calls malloc
. I am not 100% sure though, can something go wrong here?
It appears to me that in order for memory to be in your memory
container it must have been allocated with your custom allocator that always calls malloc
. Therefore I believe your code calling free should be ok.
Obviously if someone goes around stuffing random addresses into the memory map you will wind up with all sorts of undefined behavior.
Assuming the objects using your garbage collector never implement a destructor, and this holds true for any members that those objects may contain, the code as it were is "safe" in the sense that the call to free()
directly is just by-passing the work the compiler would have done to achieve the same thing as it inlined the delete
calls.
However, the code is not really safe.
If you ever changed how your garbage collector worked, or how the new
function worked, then you would have to hunt down all the direct calls to free()
to head off any problems. If the code was ever cut-and-pasted or otherwise reused in a context outside of your garbage collector, you would face a similar problem.
It is just better practice to always match new
to delete
and malloc
to free
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With