【问题标题】:How to use vector iterators when using vector<>::push_back()使用 vector<>::push_back() 时如何使用向量迭代器
【发布时间】:2014-09-12 20:05:01
【问题描述】:

为简单起见,我将坚持使用vector&lt;int&gt;,但我认为这适用于任何vector&lt;T&gt; 对象。

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

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] 的新地址,但指针@987654337 的地址@ 作为一个对象本身不会改变。但是my_vec 在任何意义上都不是指向my_vec[0] 的指针,所以我很困惑为什么my_vec[i] 的地址会改变,而对象my_vec 不会改变。

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

3) 鉴于 #2 就是这样,并且当我调用 push_back() 时我的迭代器变得毫无价值,那么处理这个问题的正确方法是什么?如果我需要使用push_back(),我应该不使用迭代器吗?如果有人要抱怨我使用迭代器和push_back() 的用例,为了简洁起见,我将其排除在外,但它基本上是使用vector&lt;int&gt; 实现堆栈,我使用迭代器来跟踪顶部堆栈。由于我不想以固定大小开始,所以当我的迭代器命中my_vec.end() 时,我尝试使用push_back() 来扩大堆栈。但我认为总的来说这是一个有效的问题。

非常感谢您的帮助!

【问题讨论】:

  • 你真的应该一次只问一个明确的问题。
  • @Blastfurnace,我根据你的评论删除了#4,但你的参考没有回答#2,这是我真正好奇的。
  • @juanchopanzo,我觉得他们都是密切相关的。
  • @river_jones:我认为最简单的解决方法是保存 indexes 而不是迭代器或指针。 my_vec[5] 将始终是第六个元素,即使在发生插入、擦除或调整大小之后也是如此。
  • 你的问题太长而且不可读,我什至没有读到最后,但是+1的声音标题,我有同样的问题。

标签: c++ vector iterator push-back


【解决方案1】:

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

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

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

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

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

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

还有其他vector&lt;T&gt; 成员函数(除了那些明显的)对迭代器有这种影响吗?

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

此外,擦除元素将使引用被擦除元素或序列中后面的元素的任何迭代器无效。

【讨论】:

  • 感谢您的帮助。另外,我将堆栈作为练习来实现,而不是重新发明轮子。再次感谢!
【解决方案2】:

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

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

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

如果我定义了一个vector 对象(例如vector&lt;int&gt; v),那么v 对象就有一个地址。当由于插入或擦除而发生重新分配时,更改的不是v 的地址,而是其成员变量buffer 的值(即buffer 将指向一个新的地址位置)。

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

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

还有其他对迭代器有这种影响的向量成员函数(除了明显的那些)吗?是否依赖于T?

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

【讨论】:

  • “其成员buffer的地址”也没有改变。 存储在buffer的地址是正在改变的地址。
猜你喜欢
  • 2013-03-21
  • 1970-01-01
  • 2011-02-16
  • 1970-01-01
  • 1970-01-01
  • 2012-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多