是否可以以便携方式使用新的数组放置?

Jam*_*and 36 c++ arrays compiler-construction portability overhead

在将它用于数组时,是否可以在便携式代码中实际使用新的放置?

看来你从new []返回的指针并不总是和你传入的地址相同(5.3.4,标准中的注释12似乎证实这是正确的),但是我不知道你是怎么回事如果是这种情况,可以为数组分配一个缓冲区.

以下示例显示了该问题.使用Visual Studio编译,此示例导致内存损坏:

#include <new>
#include <stdio.h>

class A
{
    public:

    A() : data(0) {}
    virtual ~A() {}
    int data;
};

int main()
{
    const int NUMELEMENTS=20;

    char *pBuffer = new char[NUMELEMENTS*sizeof(A)];
    A *pA = new(pBuffer) A[NUMELEMENTS];

    // With VC++, pA will be four bytes higher than pBuffer
    printf("Buffer address: %x, Array address: %x\n", pBuffer, pA);

    // Debug runtime will assert here due to heap corruption
    delete[] pBuffer;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

查看内存,编译器似乎使用缓冲区的前四个字节来存储其中项目数的计数.这意味着因为缓冲区sizeof(A)*NUMELEMENTS很大,所以数组中的最后一个元素被写入未分配的堆中.

所以问题是你能找到你的实现需要多少额外的开销来安全地使用placement new []吗?理想情况下,我需要一种可在不同编译器之间移植的技术.请注意,至少在VC的情况下,不同类的开销似乎不同.例如,如果我删除示例中的虚拟析构函数,则new []返回的地址与我传入的地址相同.

OJ.*_*OJ. 26

我个人倒有没有使用贴片阵列上新的选项去,而是在阵列中使用新的安置在每个项目单独.例如:

int main(int argc, char* argv[])
{
  const int NUMELEMENTS=20;

  char *pBuffer = new char[NUMELEMENTS*sizeof(A)];
  A *pA = (A*)pBuffer;

  for(int i = 0; i < NUMELEMENTS; ++i)
  {
    pA[i] = new (pA + i) A();
  }

  printf("Buffer address: %x, Array address: %x\n", pBuffer, pA);

  // dont forget to destroy!
  for(int i = 0; i < NUMELEMENTS; ++i)
  {
    pA[i].~A();
  }    

  delete[] pBuffer;

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

无论您使用哪种方法,请确保在删除pBuffer之前手动销毁阵列中的每个项目,因为最终可能会泄漏;)

注意:我没有编译它,但我认为它应该工作(我在没有安装C++编译器的机器上).它仍然表明了点:)希望它在某种程度上有所帮助!


编辑:

它需要跟踪元素数量的原因是,当您在数组上调用delete并确保在每个对象上调用析构函数时,它可以迭代它们.如果它不知道有多少它将无法做到这一点.

  • @JustinTime标准本身的缺陷是允许将开销应用于`void*operator new [](std :: size_t count,void*ptr);`当这个运算符被称为"no-op"时(没有分配)并且还清楚地知道这个操作符或其标量兄弟返回的指针不能传递给`delete`或`delete []`,要求程序员手动销毁每个元素. (4认同)
  • @bit2shift C++ 标准明确指出 `new[]` 填充分配的内存,尽管它允许填充 0 字节。(参见“expr.new”部分,特别是示例(14.3)和(14.4)以及它们下面的解释。)因此,在这方面,它实际上是符合标准的。\[如果您没有 C++14 标准最终版本的副本,请参阅第 133 页[此处](http://www.open-std.org/jtc1/sc22/wg21/docs/papers /2014/n4296.pdf).\] (2认同)
  • @JustinTime这是一个缺陷似乎没有人盯着看. (2认同)
  • 关于将在连续内存中单独构造的相邻对象的集合视为数组对象,请参阅 [C++ Core Issue 2182](https://groups.google.com/a/isocpp.org/d/topic/std-discussion /bsb8okPgDak/讨论)。不幸的是,这似乎还没有真正得到解决,这意味着可能没有任何方法可以就地构造一个保证按标准工作而没有任何未定义行为的数组对象。[此视频](https://youtu.be/IAdLwUXRUvg?t=871)也分析了这个问题。 (2认同)

Jam*_*and 5

@德里克

5.3.4,第 12 节讨论了数组分配开销,除非我误读了它,否则它似乎向我表明编译器将它添加到新布局也是有效的:

此开销可应用于所有数组 new 表达式,包括那些引用库函数 operator new[](std::size_t, void*) 和其他放置分配函数的表达式。从一个 new 调用到另一个调用,开销的数量可能会有所不同。

也就是说,我认为 VC 是唯一给我带来麻烦的编译器,其中包括 GCC、Codewarrior 和 ProDG。不过,我必须再次检查才能确定。

  • @MooingDuck 没有放置删除运算符来使用上述“数组长度”。实际上,您必须手动调用具有 `new(pointer) type[length]` 的数组 **constructed** 的析构函数。VC 是一个糟糕的编译器,每个人都应该知道。 (3认同)