C++中全局operator delete的两种变体是什么?跨编译器兼容是否需要同时实现这两个版本?
operator delete Overloads, and How to Make It Cross-Compiler Compatible? Great question—this is a super common gotcha when working with global operator delete overloads across different C++ compilers. Let’s break this down step by step.
First, let’s recap the two versions you’re seeing:
void operator delete(void*) noexcept: The original, C++98-era global delete operator.void operator delete(void*, std::size_t) noexcept: Introduced in C++11, this overload lets the compiler pass the size of the deleted object, which can optimize memory deallocation (especially for arrays or when matching a correspondingoperator new(std::size_t)overload).
Why GCC and Clang Behave Differently
The behavior you’re seeing comes down to differences in how the standard library implementations (libstdc++ for GCC, libc++ for Clang) implement std::make_shared.
When std::make_shared allocates memory for both the object and the control block, the way it deallocates that memory when the shared pointer goes out of scope varies between libraries. GCC’s libstdc++ chooses to call the size_t-overloaded version, while Clang’s libc++ uses the plain void* version. Both are valid under the C++ standard—there’s no requirement for which overload must be called here, so each library makes its own choice.
Do You Need to Provide Both Overloads for Cross-Compiler Compatibility?
Yes, absolutely. If you want your code to work reliably across GCC, Clang, MSVC, and other compilers, you should implement both versions of the global operator delete.
Here’s why:
- Different standard libraries will call different overloads depending on their implementation details.
- Even within a single compiler, there are scenarios where each overload might be used:
- The
size_toverload is typically called when the compiler knows the exact size of the object being deleted (e.g., non-polymorphic types, or when deleting a derived object through a base pointer with a virtual destructor). - The plain
void*overload may be called when the object size isn’t known at compile time (though this often ties to undefined behavior, like deleting a polymorphic object through a non-virtual base pointer).
- The
To avoid code duplication, you can have one overload delegate to the other. For example:
#include <iostream> #include <memory> void operator delete(void* ptr) noexcept { std::cout << "delete1 "; // Your actual memory deallocation logic here } void operator delete(void* ptr, std::size_t) noexcept { std::cout << "delete2 "; operator delete(ptr); // Reuse the plain delete logic } int main() { auto x = std::make_shared<int>(); }
This way, no matter which overload gets called, you’re executing the same core deallocation logic, and you’ll avoid linker errors or unexpected behavior across compilers.
内容的提问来源于stack exchange,提问作者user5545734

