当我从地图中的向量推送对象的引用时,引用向量中的先前值变为垃圾,但原始对象不会。
以下是重现问题的最小代码:
#include <iostream>
#include <vector>
#include <map>
#include <string>
class foo
{
private:
std::map<std::string, std::vector<int>> _allObjs;
std::vector<int*> _someObjs;
public:
void addObj(const std::string &name, int obj)
{
_allObjs[name].push_back(obj);
_someObjs.push_back(&_allObjs[name].back());
}
void doStuff()
{
for (auto &obj : _someObjs)
{
std::cout << *obj << std::endl;
}
}
};
int main()
{
foo test;
test.addObj("test1", 5);
test.addObj("test1", 6);
test.addObj("test2", 7);
test.addObj("test2", 8);
test.doStuff();
}
Expected Output
5
6
7
8
Actual Output
-572662307
6
-572662307
8
调试它时,我发现指针在addObj中将对象推送到_allObjs
后变为垃圾。我不知道造成这种情况的原因是什么,所以我不能在那里提供很多帮助。谢谢!
答案 0 :(得分:5)
向量将其数据存储在连续的内存块中。
当你想要存储超过它当前容量的容量时,它将分配一个新的,更大的连续内存块,并将所有现有元素从前一块内存复制/移动到新内存块中。
当您存储指向您的整数(&_allObjs[name].back()
)的指针时,您将int
的内存地址存储在其中一个内存块中。
一旦向量增长到需要创建额外空间的大小,所有这些内存地址都将指向解除分配的地址。访问它们是不确定的行为。
答案 1 :(得分:4)
让我们看看this reference page关于将新对象插入向量的内容:
如果new size()大于capacity(),那么所有迭代器和引用(包括过去的迭代器)都将失效。否则只有过去的迭代器无效。
因此,当您添加一个新对象时,先前存储的引用同一向量中的对象的指针可能会变为无效,即它们不再指向有效对象(除非您确保不超过容量,否则您没有“T)。
答案 2 :(得分:3)
中的指针
std::vector<int*> _someObjs;
不稳定。
使用时
_allObjs[name].push_back(obj);
由于重新分配,之前获得的任何地址都可能无效。
正如reference所述:
如果新
size()
大于capacity()
,则所有迭代器和引用(包括过去的迭代器)都将失效。否则只有过去的迭代器无效。
答案 3 :(得分:2)
正如其他人正确提到的那样,向vector
添加项可能会使调整器和调整vector
的大小调整无效。
如果您希望通过最少的代码更改来快速缓解情况,请从
更改 std::map<std::string, std::vector<int>>
到
std::map<std::string, std::forward_list<int>>
要么
std::map<std::string, std::list<int>>
由于std::list / std::forward_list
在调整列表大小时不会使迭代器和引用无效,因此上述场景应该有效(唯一将失效的迭代器是指向您已从列表中删除的项的迭代器)。
请注意,缺点是使用链接列表不会将项目存储在连续内存中,而不像std::vector
,std::list
会占用更多内存。
答案 4 :(得分:1)
这:_allObjs[name].push_back(obj);
(和另一个push_back
)可能使vector
中的所有迭代器(和指针)无效。之后你不能假设任何。