2

问题:如何使用“ placement new ”来创建具有动态大小的数组?或者更具体地说,如何从预先分配的内存中为数组元素分配内存。

我正在使用以下代码:

void* void_array = malloc(sizeof(Int)); 
Int* final_array = new(void_array) Int;

这保证了 final_array*(数组指针)是从 void_array* 保留的位置分配的。但是 final_array 元素呢?我希望它们也从预先分配的内存中分配。

PS:我不得不说我正在使用一些 API,这些 API 可以让我对 tile 架构进行一些控制。有一个功能与 malloc 完全一样,但还有其他功能,例如让您控制已分配内存的属性。所以,我基本上需要做的是使用类似 malloc 的函数来分配具有我想要的属性的内存(例如,从哪个内存库,在哪里缓存等等)

4

2 回答 2

6

首先,让我们确保我们都同意内存分配和对象构造的分离。考虑到这一点,假设我们有足够的内存来存储对象数组:

void * mem = std::malloc(sizeof(Foo) * N);

现在,您不能使用placement array-new,因为它已损坏。正确的做法是分别构造每个元素:

for (std::size_t i = 0; i != N; ++i)
{
    new (static_cast<Foo*>(mem) + i) Foo;
}

(只有指针运算才需要强制转换。placement-new 所需的实际指针只是一个 void 指针。)

顺便说一下,这正是标准库容器的工作方式,以及标准库分配器的设计方式。关键是您已经知道元素的数量,因为您在初始内存分配中使用了它。因此,您不需要 C++ array- 提供的魔法new,它只是将数组大小存储在某处并调用构造函数和析构函数。

破坏反向进行:

for (std::size_t i = 0; i != N; ++i)
{
    (static_cast<Foo*>(mem) + i)->~Foo();
}

std::free(mem);

不过,您必须了解的另一件事是:异常安全。上面的代码实际上是不正确的,除非Foo有一个不抛出的构造函数。要正确编码,您还必须存储一个展开位置:

std::size_t cur = 0;
try
{
    for (std::size_t i = 0; i != N; ++i, ++cur)
    {
        new (static_cast<Foo*>(mem) + i) Foo;
    }
}
catch (...)
{
    for (std::size_t i = 0; i != cur; ++i)
    {
        (static_cast<Foo*>(mem) + i)->~Foo();
    }
    throw;
}
于 2012-11-06T14:56:18.970 回答
1

malloc您应该覆盖operator new()并使用它,而不是使用 custom 。这不是运算符new;有一个实际调用的函数operator new(),看起来可能令人困惑,它是普通(非放置)运算符new用来获取构造对象的原始内存的函数。当然,只有在需要特殊的内存管理时才需要覆盖;否则默认版本可以正常工作。

使用它的方法如下,假设您的数组大小为size

Int* final_array = static_cast<Int*>(size == 0 ? 0 : operator new(sizeof(Int) * size));

然后你可以独立地构造和销毁每个元素。例如,对于元素n

// Create
new(final_array + n) Int; // use whatever constructor you want

// Destroy
(final_array + n)->~Int();
于 2012-11-06T15:09:35.470 回答