c ++ 17:必须至少替换掉所有版本的全局操作符new / delete才能涵盖所有情况?

问题描述

在全局重载运算符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);
}