遍历std::map时用std::for_each插入新元素,是否会因重分配引发段错误?
遍历std::map时插入元素的安全性问题
我用C++11编写了一款AEC转WebAssembly编译器,其中包含以下代码:
if (!staticPropertiesInitialized) { basicDataTypeSizes["Integer32"] = 4; basicDataTypeSizes["Character"] = 1; basicDataTypeSizes["Decimal32"] = 4; basicDataTypeSizes["Integer64"] = 8; basicDataTypeSizes["Decimal64"] = 8; basicDataTypeSizes["Integer16"] = 2; std::for_each(basicDataTypeSizes.begin(), basicDataTypeSizes.end(), [&](std::pair<std::string, int> type) { if (not(isPointerType(type.first))) basicDataTypeSizes[type.first + "Pointer"] = 4; // JavaScript (WebAssembly) virtual machine is // 32-bit (pointers being 32 bits or 4 bytes // long), unless somebody switches to the 64-bit // mode (which is almost never done). }); basicDataTypeSizes["Nothing"] = 0; }
显然,若basicDataTypeSizes是std::vector,这种在遍历期间修改容器大小的操作可能因内存重分配引发段错误。但这里basicDataTypeSizes是std::map,它本质是红黑树结构,修改时节点应该不会被重分配?事实是否如此?我测试过的编译器均未为此代码发出警告。
解答
- 遍历
std::map时插入新元素是完全安全的,这和std::vector的行为有本质区别:std::vector采用连续内存存储,插入元素可能触发内存扩容重分配,导致所有原有迭代器失效;而std::map基于红黑树实现,每个元素都是独立的动态分配节点,插入新元素只会调整树的结构(旋转、重新着色),不会移动现有节点的内存位置,已存在的迭代器(除了被删除节点的迭代器)都会保持有效。- 你这段代码中,遍历过程中插入的
"XXXPointer"类键值对是新增的红黑树节点,不会影响正在遍历的原有节点的迭代器,因此不会出现类似std::vector的段错误问题。
- 编译器未发出警告是合理的:这种操作符合C++标准对
std::map的行为定义,不属于未定义行为,编译器没有理由触发警告。 - 额外注意:如果是在遍历
std::map时删除元素,则需要注意迭代器失效问题(比如删除当前迭代器指向的元素时,要用erase返回的新迭代器继续遍历),但你这里仅执行插入操作,不存在这个风险。
内容的提问来源于stack exchange,提问作者FlatAssembler
相关产品推荐
相关产品推荐

