Home > Software engineering >  Overriding global new/delete: do I still need a stl custom allocator for containers?
Overriding global new/delete: do I still need a stl custom allocator for containers?

Time:11-03

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 to new. 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 like std::vector<int, my_allocator<int>>? Or are the globally re-defined new/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 using malloc() within operator new, because it just makes a life of library developer so much easier. So, yes, you will need to override the malloc/free/calloc/realloc.

Second question: No

  • std::vector uses std::allocator by default, and std::allocator is required to use global operator new, that is, ::operator new(size_t) to obtain the memory. However, it isn't required to call it exactly once per call to allocator::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 by std::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::allocaterwill call the global new function in its allocatefunction, 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);
}
  • Related