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

时间:2014-04-19 14:21:22

标签: c++ pointers memory casting new-operator

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

本文中描述的一个实用程序模板声明如下:

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执行与void指针转换有什么不同吗?
  • 两者之间有任何性能差异吗?在G ++中,我无法找到速度上的任何显着差异
  • 第二个代码示例是否有任何漏洞(除了我通过断言检查的潜在空指针解除引用)?

问候,TM3P

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:2)

  

new会做什么不同于void指针转换吗?

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

  

这两者之间是否有任何性能差异?

对于普通类型,初始化和赋值实际上是相同的,因此可能很少或没有区别。对于非平凡类型,它们调用不同的用户定义函数(构造函数与赋值运算符),这可能会做很多不同的事情。

  

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

对于非平凡类型,赋值运算符可能会在赋值之前对对象的状态进行假设,如果没有有效对象,可能会出现可怕的错误,将程序置于未定义的行为中。

tl; dr 放置新作品,狡猾的施法很糟糕。