67

(There were at least 4-5 topics with a similar topic on SO. I read each of them and I don't feel they really help me with this specific issue).

I'm using Visual Studio .NET 2003 on Windows 7.

I have my own overloads of new/delete that point to my own custom calls to malloc() and free() for diagnostics. My new/delete overloads are in a header file which I've included in a few files.

The problem is, the code base is pretty much spaghetti and there is no easy way to make sure these overloads get used by everything. There are includes to third party libraries that are black-box. We also use STL everywhere.

In my tests I've found that STL is still mixing calls to my own new/delete and the standard MSVC new/delete calls.

It doesn't seem realistic to include my header file in thousands of other files, that would just take far too long. Can anyone offer some tips on how to properly and effectively overload new/delete globally so everything uses my custom memory manager?

halfer
  • 19,824
  • 17
  • 99
  • 186
void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • 1
    If you define the operators globally in a pre-compiled header that should cover the most ground. Alternately you can use the CRT heap functions if this is for detecting memory leaks. – AJG85 Nov 18 '11 at 17:09

2 Answers2

94

That's not how this works. You replace the two operators, and this is done at link time. All you need to do is write a single TU that defines these operators and link it into the mix. Nobody else ever needs to know about this:

// optional_ops.cpp

void * operator new(std::size_t n) throw(std::bad_alloc)
{
  //...
}
void operator delete(void * p) throw()
{
  //...
}

In principle, there's no need for any header files to declare these functions (operator new, operator delete), since the declarations of those two functions are already hardcoded into the language, if you will. However, the names std, std::bad_alloc and std::size_t are not predeclared, so you will probably want to include <new> or some other header to provide those names.

In C++11 and beyond, you can alternatively use decltype(sizeof(0)) to get the size of the first parameter in a way that doesn't require any kind of library. C++11 also has a simpler exception model without dynamic exception specifications (which were finally removed from the language entirely in C++17).

void * operator new(decltype(sizeof(0)) n) noexcept(false)
{
  //...
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    Won't the linker complain about duplicate definitions? I think the ODR applies here. Not to mention we have 120 DLLs that we build, and I'd have to link it in each one of those DLL projects. I guess this is still better than the alternatives. – void.pointer Nov 18 '11 at 17:12
  • 3
    @RobertDailey: Nope, special case, covered by the standard, weak references, etc etc. I actually reported a bug in GCC concerning this the other day, so with the latest version this should even work with `-fwhole-program` and `-flto` and whatnot (see [here](http://stackoverflow.com/questions/7629270/how-does-stdstring-allocate-memory-in-gcc-with-fwhole-program) and [here](http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50594).)` – Kerrek SB Nov 18 '11 at 17:13
  • Could you explain what you mean by "covered by the standard" and "weak references"? Thanks!! – void.pointer Nov 18 '11 at 17:25
  • 7
    @RobertDailey: The standard covers that user definitions *replace* the functions, so there's no violation of ODR. The compiler *implements* this by making the standard functions "weak" references that are overridden by the linker if another symbol of the same name is found. – Kerrek SB Nov 18 '11 at 17:26
  • Thanks for the information. What section in the standard can this be found? I want to do some more reading on this & related rules. Thanks so much!! – void.pointer Nov 18 '11 at 17:32
  • @KerrekSB, don't forget the other 3 versions: new[], delete[], nothrow – MSN Nov 18 '11 at 18:03
  • @MSN, more like, power-3: throw/nothrow, scalar/array, new/delete. Yep. Plenty of code :-) But at least you don't *break* anything if you always keep each new/delete pair together. – Kerrek SB Nov 18 '11 at 18:05
  • Does this same technique work for replacing malloc() and free() on Windows? – void.pointer Nov 18 '11 at 18:08
  • @RobertDailey: `malloc()` lives on different level. Usually `malloc()` is actually used directly by the C++ allocation functions. Since `malloc()` is part of your C library, you have to convince the linker to prefer a different implementation. On Linux that's trivial with `LD_PRELOAD`, and it's a piece of cake there to try out any of the competing allocators (`tcmalloc` is pretty dramatic). – Kerrek SB Nov 18 '11 at 18:11
  • 5
    Yeah, on Windows you can't offer up replacement new/delete functions for the whole program without linking them into every DLL. Or at least forcing the dlls to import those functions, but I'm not sure if that works. I think it's unfortunate that DLLs work this way. On other platforms when shared libraries are linked at program start-up time they generally obey the same rules as statically linked translation units, so if your program replaces new/delete the shared libraries will get those replacements too. – bames53 Nov 18 '11 at 19:08
  • Do they need to be defined in a cpp? Can't they be inlined? – Abhishek Jain Jul 31 '15 at 09:00
  • @AbhishekJain: Since they affect the entire program, no, they cannot generally be inlined. Linkers need to provide some special magic ("weak linkage") to allow this replacement mechanism. – Kerrek SB Jul 31 '15 at 09:05
  • 5
    You might also want to override the "no throw" versions of `new`, which have a second parameter `const std::nothrow_t&` (part of the C++ standard) – rustyx Oct 02 '15 at 12:34
  • Is there some documentation on *all* the new/delete's that could/should be implemented? throws, nothrows, arrays etc edit: this perhaps; http://en.cppreference.com/w/cpp/memory/new/operator_new – Soylent Graham Feb 25 '16 at 12:54
  • @SoylentGraham: The most direct way may be to read section [support.dynamic] in the Standard. That gives a comprehensive description. – Kerrek SB Feb 25 '16 at 13:15
  • When I first tried to replace new/delete, the process crashed before it could break at _start()...or so the debug session said. The process ran without issue after I added #include , and removed //#include //using namespace std; Note: this is on an arm v5 target. – stephen Nov 16 '17 at 01:00
  • @stephen: The names `std` and `std::size_t` aren't pre-declared, so it is up to you to make them declared in the TU where you define the replacement functions; typically via `#include `. But users don't need to include that header. – Kerrek SB Nov 16 '17 at 01:41
  • @KerrekSB Exactly. What's interesting to me is the program compiled and immediate went off into the weeds prior to my modification. It's as if std or allowed for some inappropriate code. – stephen Nov 16 '17 at 18:56
  • Does this solution work across shared libraries under Linux? – Andreas Pasternak Aug 07 '18 at 21:54
  • @AndreasPasternak: Do you mean shared libraries loaded at runtime via `dlopen`? I'm not sure, but I think so, at least as long as the shared library doesn't itself replace the operators. The built-in definitions typically are implemented with "weak linkage", and I imagine that that continues to work during dynamic loading. But best to try it out yourself! – Kerrek SB Aug 07 '18 at 22:07
  • @KerrekSB: No, I just link everything together at compile time. Unfortunately, it does not work across shared libraries if I do not use `LD_PRELOAD`. See my question here: https://stackoverflow.com/questions/51735795/globally-override-new-and-delete-in-all-linked-shared-libraries?noredirect=1#comment90432638_51735795 – Andreas Pasternak Aug 07 '18 at 22:10
  • @AndreasPasternak: So you link everything in one step, and one of your libraries replaces the operators, and the replaced functions end up not getting called? Or do they only get called by code in the library that replaces them? I'm not sure, I'd look for "weak linkage" in your compiler's documentation. – Kerrek SB Aug 07 '18 at 22:42
  • @KerrekSB: I build one shared library which contains the overloaded new/delete operators. Then I build another simple shared library. I then link everything together to an executable. new/deletes in the executable cpp are properly handled but new/deletes in the shared library are not. If I preload my small shared library which contains new/delete with `LD_PRELOAD` it works tough and new/delete in the other shared library is properly handled. I will check "weak linkage", thank you – Andreas Pasternak Aug 07 '18 at 22:54
44

Also add these lines:

void *operator new[](std::size_t s) throw(std::bad_alloc)
{
    // TODO: implement
    return NULL;
}
void operator delete[](void *p) throw()
{
    // TODO: implement
}
Extrunder
  • 541
  • 3
  • 3