std::vector::reserve 之后分配内存的 memset

memset of allocated memory after std::vector::reserve

关于这个主题已经有一个密切相关的问题 here,但是这个问题的争议很大,相关的讨论让我有点困惑。 那么下面的思路对吗?

我的情况是这样的: 我有一个使用块来存储数据的数据结构。我想使用 std::vector<ChunkT> myChunks; myChunks.reserve(1000000); 之类的方法预分配大量块,并在需要时使用 ChunkT* newChunk = &myChunks.emplace_back(); 获取一个无需分配的新块。我希望新块被零初始化,但我更喜欢在保留内存后直接使用 memset 进行初始化,而不是在获取它后一次初始化一个块。前提是 ChunkT 是 POD,例如struct {size_t keys[512]; size_t values[512];}; 我不确定以下内容:

  1. reserve之后使用memset对内存进行0初始化是否安全?
  2. 在使用 ChunkT* newChunk &myChunks.emplace_back() 获取我的块后,是否保证在 ChunkT 为 struct {size_t keys[512]; size_t values[512];}; 的示例中我仍然有 0 初始化的内存?

关于 1.) 链接问题中的一位用户认为这是不安全的,因为该标准不保证 std::vector 实现可能会使用保留内存做什么(例如,将其用于内部簿记) . wpzdm 争辩说保留内存不会发生任何令人惊讶的事情。阅读所有相关讨论后,我现在认为仅访问保留内存中的对象是安全的,因为它们的生命周期已经开始(因为它们是 POD 并由向量的分配器分配),因此它们是完全有效的对象。然而,在内存成为“有效”范围的一部分之前,它们的内容在任何时候都无法保证,例如通过emplace_back,因为标准没有说vector实现一定不能修改保留范围(所以2.)是No?)。但矢量实现也不能依赖于那些保留对象的内容,因为我们可以根据需要访问和更改它们。因此,无论是“内部簿记”还是设置调试标志来检测“有效”之外但在保留范围内或类似内容的越界访问,都不会严格符合标准,因为它可能会导致不允许的副作用。那么只有恶意或不符合规范的编译器才会修改保留范围?

如果我将 ChunkT 更改为 struct {size_t keys[512]={0}; size_t values[512]={0};}; 那么 emplace_back 之后的对象内容是可以保证的,但是这次是因为初始化是通过构造进行的。此外,现在访问唯一保留的内存将是未定义的行为,因为对象的生命周期尚未开始。

  1. is it guaranteed that I still have 0-initialized memory in the example of ChunkT being struct {size_t keys[512]; size_t values[512];}; after fetching my chunk with ChunkT* newChunk &myChunks.emplace_back()?

emplace_back() value 初始化对象,因此无论对象创建之前包含什么内存,都可以保证零初始化。

  1. is it safe to 0-initialize the memory using memset after reserve?

也许有用,但最好不要。通过 [] 访问不存在的元素是 UB。

  1. is it guaranteed that I still have 0-initialized memory in the example of ChunkT being struct {size_t keys[512]; size_t values[512];}; after fetching my chunk with ChunkT* newChunk &myChunks.emplace_back()?

是的。在您的情况下,emplace_back() 所做的是通过 placement-new 构造一个 Chunk,POD-类 将被零初始化。 参考:POD class initialized with placement new default initialized?

因此,您不必担心 memset 分配的内存为零。如有不妥请指正