C++ - Replace global new & delete functions in project

function signature

Reference documentation:new - delete

Version replaceable allocation functions and replaceable deallocation functions are implicitly declared in each translation unit even if the standard library header is not included. Versioned replaceable allocation functions and replaceable deallocation functions are replaceable: a user-supplied non-member function with the same signature defined anywhere in the program and in any source file replaces the default version. Its declaration does not need to be visible.

This means that when we replace the global new/delete functions, we do not need to declare them in .h, we only need to define them in .cpp.

If a program provides multiple substitutions for a function that can be substituted, or if it has a substitution declaration with an inline specifier, the program is ill-formed and does not require diagnosis. If a substitution is defined in a namespace other than the global namespace, or if it is defined as a static non-member function in the global scope, then the program is ill-formed.

For the same module, the replaced global new/delete function can only have one implementation, and the specifier must be the same as the standard library requirements.

The nothrow version of the standard library implementation directly calls the corresponding throwing version. The standard library implementation of the thrown array version directly calls the corresponding single object version. The standard library implementation with a size deallocation function directly calls the corresponding function without a size deallocation. So just replace the throw single object allocation function to handle all allocations. (since C++11)

This means that we only need to replace 4 versions of the function to implement all functions, as follows.

void* operator new(std::size_t count);
void* operator new(std::size_t count, std::align_val_t al);
void operator delete(void* ptr) noexcept;
void operator delete(void* ptr, std::align_val_t al) noexcept;
  • universal allocation function
  • Allocation functions with memory alignment requirements
  • Universal deallocation function
  • Deallocation functions with memory alignment requirements

NOTE: Versions without alignment parameters should be aligned to __STDCPP_DEFAULT_NEW_ALIGNMENT__ .

How to replace

According to the above analysis, we only need to replace the above four functions in .cpp. However, when used in practice, we will find that this solution is only effective in the module where .cpp is located. When crossing module boundaries, the replacement The new/delete functions may not be exported, and whether the functions we replace cross boundaries is compiler-specific, so a cross-platform solution is needed.

Here we choose the solution used in the Unreal engine, define a macro, complete the replacement of the new/delete function in the macro, and then require all modules to place this macro in their own .cpp. Below is a possible implementation.

#define REPLACEMENT_OPERATOR_NEW_AND_DELETE                                                                                                 \
    void* operator new(std::size_t Count)                             { return FMemory::Malloc(Count, __STDCPP_DEFAULT_NEW_ALIGNMENT__); } \
    void* operator new(std::size_t Count, std::align_val_t Alignment) { return FMemory::Malloc(Count, static_cast<size_t>(Alignment));    } \
    void operator delete(void* Ptr)                             noexcept { FMemory::Free(Ptr); }                                            \
    void operator delete(void* Ptr, std::align_val_t Alignment) noexcept { FMemory::Free(Ptr); }

Post Reply