使用std::list的Placement new

4

我希望实现一个(双向)链表,其中placement new仅在内部调用,将所有内存指向使用类似下面的方法分配的池:

char *memPool = new char[4096]; // One-off normal 'new'

最初我打算实现自己的类,该类接受指向(管理类)预分配内存池的指针。但是我想先确定是否可以通过 std::list 实现相同的结果。特别是,David Rodríguez 在此 SO 问题的第三部分中提到的内容令我感到担忧。

std::list 需要在其组件节点上调用 newdelete,这很有道理,但我想修改此行为,使得所有要分配的节点都使用 placement new 放置到我的自定义池中。因此,我的问题是:

有没有一种方法可以指定一个 placement new std::list,例如:

std::list<std::shared_ptr<Cls>> myList = new (pool.getFreeAddr()) list<Cls>;

应该使用自定义分配器来分配节点,以便将所有内容严格存储在自己的内存池中。

注意:如果我希望在自定义内存池中使用shared_ptrs,则需要使用自定义分配/删除函数。)

1个回答

5

您需要:

  • 编写类模板MyAllocator,以满足标准中的Allocator要求([allocator.requirements])。
  • 使用std::list<T, MyAllocator<T>>代替std::list<T>

如果您需要列表的类型特别为std::list<T>(例如,因为您想调用接受std::list<T> &并且其接口不能更改的函数),那么您就会遇到麻烦,因为分配器类型是容器类型的一部分。

请注意分配器要求,它们很奇怪。特别是对于list,您将需要rebind,这有点棘手。

此外,在C++03中,没有保证分配器实例得到尊重,只有分配器类型,这实际上意味着从中分配内存的池的指针需要存储具有静态持续时间而不是作为实例变量。我认为在C++11中已经改变了这一点,但我可能错了。它只在您想在程序中使用多个不同的池时才真正重要。


编写 rebind 很简单。其背后的逻辑却很棘手。 - Pete Becker
@PeteBecker:说得好。但这意味着,例如您的分配器不能假定(可能作为优化)它仅与您指定给容器的类型T一起使用。我想我不必提到这种技巧性 - 鉴于它被链表使用,并且“重新绑定”只是机制,这个事实应该是显而易见的。 - Steve Jessop
1
不,应该提到;只是诀窍在于它的作用,而不是实际编写它。 - Pete Becker
@SteveJessop:在C++11中,您确实可以拥有有状态的分配器(并且在容器的复制/移动情况下,其背后的逻辑确实有效)。 - Matthieu M.
看起来我将开始编写一个符合标准的分配器!很高兴,重构不接受 std::list<T> & 的方法不应该是个问题。感谢您提供的所有信息! - Ephemera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接