可以以便携的方式使用新的arrays吗?

在使用数组的时候是否可以在可移植代码中实际使用新的位置?

看起来你从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; } 

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

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

就个人而言,我会select不使用arrays新的arrays,而是单独使用数组中的每个项目的新的位置。 例如:

 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; } 

无论使用什么方法,在删除pBuffer之前,请确保手动销毁数组中的每个项目,因为最后可能会泄漏;)

注意 :我没有编译这个,但我认为它应该工作(我在一台没有安装C ++编译器的机器上)。 它仍然表明这一点:)希望它以某种方式帮助!


编辑:

它需要跟踪元素数量的原因是,当你调用数组的delete时,可以遍历它们,并确保在每个对象上调用析构函数。 如果不知道有多less,就不能这样做。

@Derek

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

这个开销可以应用于所有数组的新expression式,包括那些引用库函数操作符new [](std :: size_t,void *)和其他位置分配函数的expression式。 从一个新的调用到另一个调用的开销可能不同。

这就是说,我认为VC是唯一一个给我带来麻烦的编译器,GCC,Codewarrior和ProDG。 不过,我必须再次确认。

放置新本身是可移植的,但是您对特定内存块所做的假设是不可移植的。 就像之前所说的那样,如果你是一个编译器,并且被赋予了一大块内存,那么如果你只有一个指针,你怎么知道如何分配一个数组并正确地破坏每个元素呢? (见操作员删除[]的界面。)

编辑:

实际上有一个位置删除,只有当一个构造函数抛出一个exception,而分配一个数组与放置new []时才被调用。

新的[]实际上是否需要以某种方式logging元素的数量是由标准决定的,这留给了编译器。 不幸的是,在这种情况下。

@詹姆士

我甚至不清楚为什么它需要额外的数据,因为你不会在数组上调用delete [],所以我不完全明白为什么它需要知道它有多less项。

给了这个想法之后,我同意你的看法。 没有任何理由为什么放置新应该需要存储元素的数量,因为没有放置删除。 由于没有放置删除,因此没有理由放置新元素来存储元素的数量。

我也在我的Mac上用gcctesting了这个,使用一个具有析构函数的类。 在我的系统上,新的放置不会改变指针。 这让我想知道这是否是一个VC ++的问题,以及这是否违反了标准(标准没有具体说明这一点,只要我能find)。

我认为gcc和MSVC做的一样,但是这当然不会使它变得“便携”。

我认为当NUMELEMENTS确实是一个编译时间常量时,你可以解决这个问题,就像这样:

typedef A Arr[NUMELEMENTS];

A * p = new(buffer)Arr;

这应该使用新的标量位置。

类似于您如何使用单个元素来计算一个展示位置的大小(新build),请使用这些元素的数组来计算数组所需的大小。

如果您需要其他计算的大小,其中可能不知道元素的数量,则可以使用sizeof(A [1])并乘以所需的元素数。

例如

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

感谢您的答复。 在arrays中为每个项目使用新的位置是我遇到这个问题时最终使用的解决scheme(对不起,应该在问题中提到)。 我只是觉得一定是有一些东西,我失踪了做与安置新的[]。 实际上,由于标准允许编译器向数组添加额外的未指定开销,所以放置new []基本上不可用。 我不明白你怎么能够安全和便携地使用它。

我甚至不清楚为什么它需要额外的数据,因为你不会在数组上调用delete [],所以我不完全明白为什么它需要知道它有多less项。