当推送一个新对象时,指向该对象的指针将变成垃圾

When pushing a new object, a pointer to that object becomes garbage

本文关键字:对象 指针 一个 新对象      更新时间:2023-10-16

当我从映射中的向量推送对对象的引用时,引用向量中的前一个值会变成垃圾,但原始对象不会。

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

#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,指针就会变成垃圾。我不知道是什么原因造成的,所以我帮不了什么忙。谢谢

向量将其数据存储在连续的内存块中。

当您想要存储超出其当前容量的内容时,它将分配一个新的、更大的连续内存块,并将所有现有元素从上一个内存块复制/移动到新的内存块中。

当您存储指向int(&_allObjs[name].back())的指针时,您将把int的内存地址存储在这些内存块之一中。

一旦向量增长到需要创建额外空间的大小,所有这些内存地址都将指向已释放的地址。访问它们是未定义的行为。

让我们看看这个参考页面对向向量插入新对象的说明:

如果新的size()大于capacity(),则所有迭代器和引用(包括过去的结束迭代器)都将无效。否则,只有过去的结束迭代器无效。

因此,当您添加新对象时,以前存储的指向同一向量中对象的指针可能会变为无效,即它们不再指向有效对象(除非您确保没有超过容量,但您没有超过)。

中的指针

std::vector<int*> _someObjs;

不稳定。

当你使用

_allObjs[name].push_back(obj);

先前获得的任何地址都可能由于重新分配而无效。

如参考文献所述:

如果新的size()大于capacity(),则所有迭代器和引用(包括过去的结束迭代器)都无效。否则,只有过去的结束迭代器无效。

正如其他人正确提到的,向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::list 的示例

注意,缺点是与std::vector不同,链表的使用不会将项目存储在连续内存中,并且std::list占用更多内存。

这:_allObjs[name].push_back(obj);(和其他push_back)可能会使vector中的所有迭代器(和指针)无效。之后,您不能对进行任何假设。