c – 如何正确调用对齐的新/删除?

如何调整新运算符?

auto foo = new(std::align_val_t(32)) Foo; //?

然后,如何正确删除它?

delete(std::align_val_t(32), foo); //?

如果这是使用这些重载的正确形式,为什么valgring抱怨不匹配free()/ delete / delete []?

最佳答案 存在非常基本的原则 – 无记忆存储程序总是必须匹配分配例程.如果我们使用不匹配分配和免费 – 运行时行为可以是任何:所有可以是随机的,或运行时崩溃,或内存泄漏,或堆损坏.

如果我们使用operator new的对齐版本分配内存

void* operator new  ( std::size_t count, std::align_val_t al);

我们必须使用operator delete的相应对齐版本

void operator delete  ( void* ptr, std::align_val_t al );

call void operator delete(void * ptr);这里总是必须导致运行时错误.让我们试试吧

    std::align_val_t al = (std::align_val_t)256;
    if (void* pv = operator new(8, al))
    {
        operator delete(pv, al);
        //operator delete(pv); this line crash, or silently corrupt heap
    }

为什么对齐和不对齐的运算符删除版本始终不兼容?让我们思考 – 如何在一些价值记忆中分配对齐?我们最初总是分配一些内存块. for return align指针使用 – 我们需要将已分配的内存指针调整为多重对齐.好.通过分配比请求更多的内存并调整指针,这是可能的.但现在提问 – 这个街区有多免费?一般用户得到的指针不是分配内存的开头 – 如何从这个用户指针跳回到分配块的开始?没有额外的信息这是不可能的.在用户返回指针之前,我们需要存储指针到实际分配的内存可能这将在代码典型实现中更加明显,用于对齐new和delete使用_aligned_malloc_aligned_free

void* operator new(size_t size, std::align_val_t al)
{
    return _aligned_malloc(size, static_cast<size_t>(al));
}

void operator delete  (void * p, std::align_val_t al)
{
    _aligned_free(p);
}

当没有对齐new和delete时使用malloc和free

void* operator new(size_t size)
{
    return malloc(size);
}

void operator delete  (void * p)
{
    free(p);
}

现在让我们来看看_aligned_malloc_aligned_free的内部实现

void* __cdecl _aligned_malloc(size_t size, size_t alignment)
{
    if (!alignment || ((alignment - 1) & alignment))
    {
        // alignment is not a power of 2 or is zero
        return 0;
    }

    union {
        void* pv;
        void** ppv;
        uintptr_t up;
    };

    if (void* buf = malloc(size + sizeof(void*) + --alignment))
    {
        pv = buf;
        up = (up + sizeof(void*) + alignment) & ~alignment;
        ppv[-1] = buf;

        return pv;
    }

    return 0;
}

void __cdecl _aligned_free(void * pv)
{
    if (pv)
    {
        free(((void**)pv)[-1]);
    }
}

通常,_aligned_malloc分配大小sizeof(void *)alignment – 1而不是由调用者大小请求.调整分配的指针以适应对齐,并在指针返回调用者之前存储最初分配的内存.

和_aligned_free(pv)调用not free(pv)但是free(((void **)pv)[ – 1]); – 总是另一个指针.因为_aligned_free(pv)的这种效果总是另一种比较自由(pv).和operator delete(pv,al);始终与operator delete(pv)不兼容;如果说删除[]通常具有与删除相同的效果但是对齐vs不对齐总是运行时间不同.

点赞