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

时间:2014-07-22 06:00:44

标签: c++ vector iterator 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 << "\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

显然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()来扩大堆栈。但我认为这是一个有效的问题。

非常感谢你的帮助!

2 个答案:

答案 0 :(得分:7)

  

为什么my_vec的地址没有变化?

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

  

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

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

  

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

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

  

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

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

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

答案 1 :(得分:1)

  

为什么my_vec的地址不会改变?

std::vector中的内存重新分配发生在其内部缓冲区中,该内部缓冲区保存其元素而不是对象本身(即,不是std::vector本身)。请考虑以下玩具示例:

template<typename T>
class vector {
  T *buffer;
  ...
public:
  ...
};

如果我定义vector对象(例如vector<int> v),则对象v会有一个地址。当由于插入或擦除而发生重新分配时,v的地址不会发生变化,而是其成员变量buffer的值(即buffer将指向新地址位置)。

  

为什么更改my_vec [i]地址的向量内部的任何操作(例如push_back())也不能正确“更新”任何迭代器?

iterator invalidation的问题众所周知,当你必须处理这种情况时,你应该采取预防措施。

  

是否有任何其他向量成员函数(除了显而易见的函数)对迭代器有影响?它取决于T?

是的(例如,std::vector::insertstd::vector::erase)。不,它不依赖于T(即std::vector元素的类型)。