In order to track our memory usage and display some runtime statistics to the user (in a performant way) I'm overriding global new/delete (with a #define in a header - adding that header on top of every source file where we need to allocate/deallocate memory is going to ensure we can track all allocations going on).
I have two questions at this point since it's a multi-platform C codebase:
- this is old code and some places still use
malloc
next tonew
. I think I also need to override global malloc/free/calloc/realloc, is that correct? - STL containers: we use them a lot (e.g.
std::vector
). If I include my re-#defining header at the top of every source file, do I still need to pass a custom allocator likestd::vector<int, my_allocator<int>>
? Or are the globally re-definednew/delete
enough? I think I still need the custom allocator but I'm not sure.
CodePudding user response:
First question: Yes
- The details of how operator
new
is implemented are property of a particular implementation of standard library - not even a compiler or operation system. All of them, at least, the three big ones, (CLang, GCC and MSVC) are usingmalloc()
within operatornew
, because it just makes a life of library developer so much easier. So, yes, you will need to override themalloc/free/calloc/realloc
.
Second question: No
std::vector
usesstd::allocator
by default, andstd::allocator
is required to use global operatornew
, that is,::operator new(size_t)
to obtain the memory. However, it isn't required to call it exactly once per call toallocator::allocate
.If you replace global operator
new
, then vector will use it, although not necessarily in a way that really allows your implementation to manage memory "efficiently". Any special tricks you want to use could, in principle, be made completely irrelevant bystd::allocator
grabbing memory in 10MB chunks and sub-allocating.
You are fighting against a legacy old codebase. Any pure text replacement, like #define
preprocessor directives are really discouraged (luckily, some day C will get rid out of preprocessor), but, in your concrete case, it could be a viable approach.
CodePudding user response:
If containers, like for example std::vector
use the default allocater std::allocater
as described here, then the std::allocater
will call the global new function in its allocate
function, as described here.
Allocates n * sizeof(T) bytes of uninitialized storage by calling ::operator new(std::size_t) or ::operator new(std::size_t, std::align_val_t) (since C 17)
So, it will call the global new
and if you override it then your implementation will be called. Please see the below example:
#include <iostream>
#include <iostream>
#include <vector>
#include <stdlib.h>
using namespace std;
void * operator new(size_t size)
{
cout << "New operator overloading " << endl;
void * p = malloc(size);
return p;
}
int main()
{
std::vector<int> v{};
v.resize(50);
v.resize(1500);
}