【问题标题】:Dynamic allocation with C++'s "placement new"使用 C++ 的“新位置”进行动态分配
【发布时间】:2012-11-06 14:50:56
【问题描述】:

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

我正在使用以下代码:

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

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

P.S:我不得不说我正在使用一些 API 来控制磁贴架构。有一个功能与 malloc 完全一样,但也有其他功能,例如允许您控制已分配内存的属性。所以,我基本上需要做的是使用类似 malloc 的函数来分配具有我想要的属性的内存(例如,从哪个内存库,在哪里缓存等等)

【问题讨论】:

  • 这已经是错误的了。如果您要在某处放置新的Int,则需要在某处分配sizeof(Int) 空间,而不是sizeof(Int*)。另外,查找自定义分配器 + std::vector<>std::list<>
  • 分配非固定大小是没有意义的。当你分配内存时,你需要告诉分配器你需要多少。
  • @Gorpic:这在我的应用程序中很有意义。我正在使用一个 API,它可以让我对硬件进行一些内存管理(从哪里分配内存对我来说更重要,而不是多少)
  • 这就像去旅馆说“我下个月需要房间”。除非我告诉他们我需要多少,否则他们无法保证他们会拥有它们。此外,在这种情况下,我是说“我需要具有相关数字的房间”,因为数组内存块必须是连续的。
  • @Gorpik:我希望我能在这里解释整个模型。但这项研究是关于要求一个内存库分配您需要的内存。如果不能,它会自动将您的请求发送到另一个存储库。但重要的部分是,大多数时候它都有你需要的内存,如果系统已经分离了你的请求流量,那么作为程序员的你。

标签: c++ malloc dynamic-arrays placement-new


【解决方案1】:

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

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

现在,您不能使用放置数组新,because it is broken。正确的做法是分别构造每个元素:

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

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

顺便说一下,这正是标准库容器的工作方式,以及标准库分配器的设计方式。关键是您已经知道元素的数量,因为您在初始内存分配中使用了它。因此,您不需要 C++ 数组-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;
}

【讨论】:

  • 为什么放置新的数组需要额外的内存?这是没有意义的——因为我们永远不会在那个数组上调用delete[]。我知道对于“正常”新的这种开销需要存储例如数组大小 - 但是为什么使用新位置创建的数组需要这种开销?
  • @PiotrNycz:原因很复杂,很无聊,需要研究标准的文本。阅读附加问题以了解详细信息。 (我同意这是没有意义的!这是标准中的一个缺陷,所以没有人费心提交修复请求。)
  • @Kerrek:非常感谢。那么,您是否同意在这种情况下使用向量是个好主意?或者同样的薄也发生在那里?我的意思是有什么方法不需要为每个元素分配空间,我先做一次它适用于所有其他元素?
  • @TOWI_Parallelism:std::vector 的工作方式或多或少与我在回答中描述的方式完全相同,所以是的,请使用它,不要重新发明轮子。我不明白你的最后一个问题。
  • @TOWI_Parallelism:阅读linked question。细节是技术性的和无聊的,这个评论太长了。相信我的话。相信我,我是一条鱼。 (或者来聊天室问吧!)
【解决方案2】:

您应该覆盖operator new() 并使用它,而不是使用自定义malloc。这不是运营商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();

【讨论】:

  • 替换operator new 是一个不错的主意,但如果他担心用C 编写的库中的分配,这将无济于事。虽然标准中不能保证标准@987654331 @ 将使用malloc,我从未见过没有使用的情况。
  • @JamesKanze:我的回答是尽可能地采用C++ 方式。如果涉及到 C,那么当然可以忘记 operator new()
猜你喜欢
  • 1970-01-01
  • 2016-01-10
  • 2016-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-07
  • 1970-01-01
相关资源
最近更新 更多