std::unordered_set 中的元素如何存储在C++内存中?
How are elements in an std::unordered_set stored in memory in C++?
在弄乱类型双关迭代器时,我遇到了这样做的能力
std::vector<int> vec{ 3, 7, 1, 8, 4 };
int* begin_i = (int*)(void*)&*vec.begin();
std::cout << "1st: " << begin_i << " = " << *begin_i << std::endl;
begin_i++;
std::cout << "2nd: " << begin_i << " = " << *begin_i << std::endl;
然后我尝试用std::unordered_set
做同样的事情:
std::unordered_set<int> set{ 3, 7, 1, 8, 4 };
for (auto& el : set)
{ // Display the order the set is currently in
std::cout << el << ", ";
}
std::cout << 'n' <<std::endl;
int* begin_i = (int*)(void*)&*set.begin();
std::cout << "1st: " << begin_i << " = " << *begin_i << std::endl;
begin_i++;
std::cout << "2nd: " << begin_i << " = " << *begin_i << std::endl;
但我得到的输出是:
4, 8, 1, 7, 3,
1st: [address] = 4
2nd: [address] = 0
我假设这是因为无序集合的元素位于内存的不同部分?考虑到我还使用基于范围的循环打印了元素的存储顺序,我在这里感到困惑。
我的问题是std::unordered_set
如何将其元素存储在内存中?将元素添加到集合中时会发生什么?它在内存中的哪个位置,如果它没有存储在一个类似数组的容器中,元素是一个接一个,它是如何跟踪的?
unordered_set
是使用外部链接实现为哈希表的。
这基本上意味着你有一个链表数组(通常称为"桶"(。因此,要将项目添加到unordered_set
首先对要插入的新项目进行哈希处理。然后,您获取该哈希并将其减小到数组当前大小的范围(随着您添加更多项目,该范围可以/将扩展(。然后,将新项目添加到该链表的尾部。
因此,根据哈希产生的值,两个连续插入的项目可能会(并且经常会(插入到表的完全不同的部分的链表中。然后链表中的节点通常会被动态分配,因此即使同一链表中的两个连续项目也可能位于完全不相关的地址。
然而,正如我在之前的回答中指出的那样,标准中实际上比大多数人似乎意识到的要多得多。正如我在那里概述的那样,可能(几乎(有可能违反期望并且仍然(有点(满足标准中的要求,但即使充其量,这样做也是相当困难的。对于大多数实际目的,您可以假设它很像链表的向量。
大多数相同的内容适用于unordered_multiset
- 唯一的根本区别是,您可以拥有具有相同键的多个项目,而不是只有一个具有特定键的项目。
同样,还有unordered_map
和unordered_multimap
,它们再次非常相似,只是它们将存储的东西分离到键和与该键关联的值中,并且当它们进行哈希处理时,只看键部分,而不是值部分(。
与其直接回答这个问题,我想解决"类型双关语"的技巧。(我把它放在引号里,因为提供的代码没有演示类型双关语。也许代码针对这个问题进行了适当的简化。无论如何,*vec.begin()
给出了一个int
,所以&*vec.begin()
是一个int*
。进一步投射到void*
然后回到int*
是净无操作。
代码利用的属性是
*(begin_i + 1) == *(vec.begin() + 1) // Using the initial value of begin_i
*(&*vec.begin() + 1) == *(vec.begin() + 1) // Without using an intermediary
这是连续迭代器的属性,它与连续容器相关联。这些是将其元素存储在相邻内存位置的容器。标准库中的连续容器是string
、array
和vector
;这些是保证您的技巧起作用的唯一标准容器。在deque
上尝试它起初似乎有效,但如果向&*begin()
添加足够的内容,尝试将失败。其他容器倾向于单独动态分配元素,因此元素的地址之间不需要有任何关系;元素通过指针而不是位置/索引链接在一起。
这样我就不会忽略所问的问题:
无序集合只是将元素组织到存储桶中。除了要求将所有具有相同哈希值的元素放置在同一个存储桶中外,对如何完成此操作没有任何要求。(这并不意味着同一存储桶中的所有元素都具有相同的哈希值。实际上,每个桶可能被实现为一个list
,而桶的容器可能是一个vector
,仅仅是因为重用代码很酷。同时,这是一个实现细节,所以它可以非常从一个编译器到另一个编译器,甚至从一个编译器版本到另一个编译器版本。没有保证。
std::unordered_set
存储其内存的方式是实现定义的。标准不在乎,只要它满足要求。
在VS版本中,它将它们存储在std::list
中(通过创建和管理其他数据提供快速访问( - 因此每个元素也有指向上一个的指针,下一个通过new
存储(至少这是我从std::list
中记得的(。
- 将字符串存储在c++中的稳定内存中
- std::原子加载和存储都需要吗
- C++:将控制台输出存储在宏中更好吗
- 使用QProcess执行命令,并将结果存储在QStringList中
- 访问存储在向量C++中的结构的多态成员
- 如何从存储在std::映射中的std::集中删除元素
- 存储模板类型以强制转换回派生<T>
- 类型总是使用其大小存储在内存中吗
- 当字符串存储在变量中时,如何将字符串转换为wchar_t
- 使用无符号字符数组有效存储内存
- 如何在cpp.中使用协议缓冲区存储大缓冲区/数组(char/int)
- 使用 pqxx 将 std::vector 存储在 postgresql 中,并从数据库中检索它
- 带结构的二维矢量:如何存储元素
- 添加存储在向量中的大整数的函数出现问题
- 从文件中读取多个字节,并将它们存储在C++中进行比较
- 在std::vector上存储带有模板的类实例
- 谷歌测试中的期望值存储在哪里
- 为什么C中的通用链表中存储的数据已损坏
- 在c++中获取两个大int,并将它们存储在数组中
- 在reactor中存储eventHandlers的最佳方式是什么