使用vector <> :: push_back()时如何使用向量迭代器

kil*_*out 1 c++ iterator vector push-back

为简单起见,我会坚持,vector<int>但我认为这适用于任何vector<T>对象.

如果我使用a vector<int>::iterator来跟踪int的向量中的某个位置然后我使用vector<int>::push_back(),则迭代器变得毫无价值.意思是,我不能将其地址用于&或取消引用它.一旦我打印出以下意义上的某些对象的地址,直接原因就有了意义:

vector<int> my_vec(1); //my_vec[0] = 0

vector<int>::iterator it = my_vec.begin(); //it -> my_vec[0], *it = my_vec[0] = 0

cout << "&my_vec = " << &my_vec << "\n";
cout << "&my_vec[0] = " << &my_vec[0] << "\n";
cout << "&it = " << &it << "\n"; //prints address, all good
cout << "*it = " << *it << "\n"; //prints 0, all good

cout << "\n\n" << pushing back..." << "\n\n";
my_vec.push_back(1);

cout << "&my_vec = " << &my_vec << "\n"; //same as before push_back()!
cout << "&my_vec[0] = " << &my_vec[0] << "\n"; //different from before push_back()!!
cout << "&it = " << &it << "\n"; //same as before push_back()
//cannot do &it or *it
Run Code Online (Sandbox Code Playgroud)

显然,地址it不会改变,但push_back()已经在内存中移动了一些东西,现在my_vec改变了不同"元素"的地址.my_vec [i]有一个新地址的事实对我有意义,但后来我有以下问题:

1)为什么不my_vec改变地址?似乎如果push_back()导致地址my_vec[i]发生变化,它也应该改变整个对象的地址.对于一个数组,my_array是一个指针,my_array[0]所以我可以想象一个操作改变每个的地址my_array[i]并更新指针指向新的地址,my_array[0]但指针的地址my_array作为一个对象本身不会改变.但是my_vec在任何意义上都不是指针,my_vec[0]所以我很困惑为什么地址my_vec[i]会改变而不是对象my_vec.

2)为什么内部的任何操作vector<int>都会改变my_vec[i](例如push_back())不正确"更新"任何迭代器的地址?这似乎是一个好主意?没有?

3)鉴于#2就是这样,当我打电话时,我的迭代器变得毫无价值push_back(),处理这个问题的正确方法是什么?如果我需要使用,我不应该使用迭代器push_back()吗?如果有人要抱怨我的用例是什么用于使用迭代器push_back(),我为了简洁而排除它,但它基本上实现了堆栈使用vector<int>,我使用迭代器来跟踪堆栈的顶部.因为我不希望启动固定大小,所以push_back()当迭代器命中时我尝试使用放大堆栈my_vec.end().但我认为这是一个有效的问题.

非常感谢您的帮助!

Mik*_*our 7

为什么不my_vec改变地址?

因为矢量对象本身在同一地址仍然是同一个对象.重新分配会更改它管理的动态数组的地址,而不是管理数组的矢量对象.

为什么内部的任何操作vector<int>都会改变my_vec[i](例如push_back())不正确"更新"任何迭代器的地址?这似乎是一个好主意?没有?

那将有(可能很大)运行时成本.向量必须跟踪所有迭代器(需要动态存储,每次创建迭代器时进行内存分配,并在向量更改时更新所有向量)或每个迭代器需要对容器的引用,在每次访问时检查,并且无法实现为简单的指针.C++通常会尽可能避免运行时成本 - 特别是在这种情况下,它们几乎总是不必要的.

处理这个问题的正确方法是什么?

有各种选择.您可以存储索引而不是迭代器.您可以使用容器,例如std::list使用稳定的迭代器(尽管效率可能相当低).如果可以对数组的大小设置上限,则可以保留该数量,以便不需要重新分配.您可以编写自己的容器(或适配器)来自动更新迭代器.对于你的堆栈,如果它确实是一个堆栈,你不需要跟踪除向量末尾之外的任何东西,所以根本不需要存储迭代器.或者你可以使用std::stack而不是重新发明它.

是否有任何其他vector<T>成员函数(除了明显的函数)对迭代器有影响?

当任何操作导致向量增长超过其当前容量时,迭代器将通过重新分配而失效.您可以使用该reserve功能控制容量.

此外,擦除元素将使任何迭代器无效,这些迭代器引用已擦除的元素或序列中的元素.