自定义内存分配器:T*指针,运算符new与void指针强制转换

Custom memory allocator: T* pointer, operator new versus void pointer cast

本文关键字:指针 new void 转换 运算符 分配器 内存 自定义      更新时间:2023-10-16

我根据gamedev.net文章中的代码创建了一些自定义内存分配器。

文章中描述的一个实用程序模板是这样声明的:

template <class T> T* allocateNew(Allocator& allocator, T& t) {
    return new (allocator.allocate(sizeof(T), alignof(T))) T(t);
}

*我将__alignof()转换为对alignof()的调用,因为我使用的是C++11

我认为这段代码为新的T对象分配内存,将t堆栈对象从引用复制到新分配的堆内存,并返回指向新对象的T*指针。

有了这个假设,我将上面的代码转换为:

template <class T> T* allocateNew(Allocator& allocator, T& t) {
    void *ptr = allocator.allocate (sizeof (T), alignof (T));
    assert(ptr && "So that I don't dereference a null pointer");
    * (T *) (ptr) = instance; // Casting void* to T* and then dereferencing
    return (T *) ptr;
}

它似乎运行得很好。我的问题是:

  • new与空指针强制转换有什么不同吗
  • 两者在性能上有什么不同吗?在G++中,我找不到任何显著的速度差异
  • 第二个代码示例是否有任何漏洞(除了我用断言检查的潜在空指针取消引用之外)

问候,TM3P

new与void指针铸造有什么不同吗?

是;它构造了一个新的对象,而不是试图分配给一个实际上并不存在的对象。

两者在性能上有什么不同吗?

对于琐碎的类型,初始化和赋值实际上是一回事,所以可能没有什么区别。对于非平凡类型,它们调用不同的用户定义函数(构造函数与赋值运算符),这可能会做非常不同的事情。

第二个代码示例是否存在任何漏洞?

对于非平凡类型,赋值运算符可能会在赋值前对对象的状态进行假设,如果没有有效的对象,则可能会出现严重错误,使程序陷入未定义的行为。

tl;dr放置新作品,不可靠的选角是不好的。

第一个版本使用placement new在新分配的内存中复制构造对象。这是将原始byes转换为对象的正确、定义明确的方法。

您的版本将字节视为已经是一个对象(通过强制转换),然后为该对象分配一个新值,从而产生未定义的行为。如果T是一个非常简单的类型,这可以,但不会比第一个版本快。如果该类型有赋值运算符,那么它将出现非常严重的错误。