使用 vector<>::p ush_back() 时如何使用矢量迭代器
How to use vector iterators when using vector<>::push_back()
为简单起见,我将坚持使用vector<int>
,但我认为这适用于任何vector<T>
对象。
如果我使用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 << "nn" << pushing back..." << "nn";
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
显然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()
。但总的来说,我认为这是个有效的问题。
非常感谢你的帮助!
为什么
my_vec
的地址没有变化?
因为vector对象本身仍然是位于同一地址的同一对象。重新分配改变的是它所管理的动态数组的地址,而不是管理该数组的vector对象的地址。
为什么
vector<int>
内部的任何改变my_vec[i]
地址的操作(如push_back()
)不能正确地"更新"任何迭代器?这似乎是个好主意?没有?
将有一个(可能很大的)运行时成本。要么vector必须跟踪所有迭代器(需要动态存储,每次创建迭代器时分配内存,并在vector改变时更新所有vector),要么每个迭代器都需要对容器的引用,每次访问都要检查,并且不能作为简单的指针实现。c++通常在可能的情况下避免运行时成本——尤其是在这种情况下,它们几乎总是不必要的。
处理这种情况的正确方法是什么?
有多种选择。您可以存储索引而不是迭代器。您可以将std::list
这样的容器与稳定迭代器一起使用(尽管这样可能效率较低)。如果可以设置数组大小的上限,则可以保留该上限,这样就不需要重新分配了。您可以编写自己的容器(或适配器)来自动更新迭代器。对于堆栈,如果它确实是一个堆栈,那么除了vector的末尾,您不需要跟踪任何其他内容,因此根本不需要存储迭代器。或者你可以使用std::stack
,而不是重新发明它。
当任何操作导致vector容器增长超过当前容量时,还有其他
vector<T>
成员函数(除了那些明显的)对迭代器有这种影响吗?
迭代器通过重新分配失效。可以通过reserve
功能控制容量。
同时,擦除元素将使引用被擦除元素的迭代器或序列后面的元素失效。
为什么my_vec的地址没有改变?
std::vector
中的内存重新分配发生在保存其元素的内部缓冲区,而不是对象本身(即不是std::vector
本身)。考虑下面的玩具示例:
template<typename T>
class vector {
T *buffer;
...
public:
...
};
如果我定义一个vector
对象(例如,vector<int> v
),对象v
有一个地址。当由于插入或擦除而发生重新分配时,改变的不是v
的地址,而是其成员变量buffer
的值(即buffer
将指向一个新的地址位置)。
为什么vector内部的任何改变my_vec[i]地址的操作(如push_back())不能正确地"更新"任何迭代器?
迭代器无效的问题是众所周知的,当你必须处理这种情况时,你应该采取预防措施。
还有其他vector成员函数(除了明显的那些)对迭代器有这种影响吗?它是否依赖于T?
有(例如,std::vector::insert
, std::vector::erase
)。不,它不依赖于T
(即,std::vector
的元素的类型)。
- 使用迭代器时如何访问对象在向量中的位置?
- 无法使用迭代器标记调度实例化模板
- 使用迭代器的模板类的构造函数
- 使用迭代器替换映射中的常量项的方法
- 如何在C++中仅使用迭代器正确迭代 3D 向量?
- 使用迭代器的自定义比较器函数
- 无法使用迭代器查找类对象。(二进制 '==': 'userInfo' 不定义此运算符)
- 使用迭代器 C++ 删除矢量中的重叠字符串
- 在C++中使用迭代器合并排序
- 如何使用迭代器填充未知大小的向量?
- 使用迭代器进行合并排序中的一个缓冲区
- 如何使用迭代器指向结构体c++的向量
- std::使用迭代器映射查找距离,程序不会终止
- 如何使用迭代器?
- 使用迭代器的二叉搜索
- 对列表中的向量使用迭代器
- 仅在使用迭代器的某些索引上生成值
- 如何使用迭代器在 c++ 中的递归函数中传递值?
- 使用迭代器成员函数是否仅适用于某些向量类型"empty()"?
- 使用迭代器打印矢量元素