问题描述
在全局重载运算符new和delete时,需要实现哪些版本才能涵盖所有情况?
在c ++ 17之前,似乎可以实现void* operator new(size_t bytes)
和void operator delete(void* ptr)
了,因为默认版本的数组版本和非抛出版本都是使用这两个函数实现的。
但是c ++ 17呢?例如https://en.cppreference.com/w/cpp/memory/new/operator_new并没有提到通过调用(3)来实现(4)(而提到(2)是通过调用(1)来实现)。
因此,在c ++ 17中,替换项必须满足对new和delete版本(具有局部覆盖的类除外)的所有调用的最少运算符吗?
解决方法
来自cppreference https://en.cppreference.com/w/cpp/memory/new/operator_new#Global_replacements(以及与operator delete
类似的注释):
nothrow版本(5-8)的标准库实现直接调用相应的throwing版本(1-4)。投掷数组版本(2,4)的标准库实现直接调用相应的单对象版本(1,3)。因此,替换抛出单个对象的分配函数足以处理所有分配。
因此您需要替换以下功能:
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;
其余的将根据这四个实现。
如果您没有等效的对齐malloc来替换对齐版本,则以下是针对非对齐版本的对齐new
/ delete
的简单实现:>
#include <cstddef>
#include <new>
#include <memory>
void* operator new(std::size_t count,std::align_val_t al) {
std::size_t align = static_cast<std::size_t>(al);
if (align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) [[unlikely]] return operator new(count);
std::size_t actually_allocating = align + count;
if (actually_allocating < count || (actually_allocating += sizeof(void*)) < (align + count)) [[unlikely]] {
// overflow
throw std::bad_alloc();
}
void* unaligned = operator new(actually_allocating);
void* aligned = unaligned;
std::align(align,aligned,actually_allocating);
// Store a pointer to the start of the aligned memory,to be retrieved by delete
::new (static_cast<void*>(static_cast<char*>(aligned) - sizeof(void*))) void*(unaligned);
return aligned;
}
void operator delete(void* ptr,std::align_val_t al) noexcept {
if (static_cast<std::size_t>(al) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) [[likely]] {
ptr = *static_cast<void**>(static_cast<void*>(static_cast<char*>(ptr) - sizeof(void*)));
}
operator delete(ptr);
}