当推送新对象时,指向该对象的指针变为垃圾

时间:2016-07-18 20:43:10

标签: c++

当我从地图中的向量推送对象的引用时,引用向量中的先前值变为垃圾,但原始对象不会。

以下是重现问题的最小代码:

#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后变为垃圾。我不知道造成这种情况的原因是什么,所以我不能在那里提供很多帮助。谢谢!

5 个答案:

答案 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在调整列表大小时不会使迭代器和引用无效,因此上述场景应该有效(唯一将失效的迭代器是指向您已从列表中删除的项的迭代器)。

Example using std::list

请注意,缺点是使用链接列表不会将项目存储在连续内存中,而不像std::vectorstd::list会占用更多内存。

答案 4 :(得分:1)

这:_allObjs[name].push_back(obj);(和另一个push_back)可能使vector中的所有迭代器(和指针)无效。之后你不能假设任何