How to properly replace global new & delete operators

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)
{
  //...
}

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
}