【问题标题】:C++ Iterator randomly gets invalidatedC++ 迭代器随机失效
【发布时间】:2011-12-06 16:09:53
【问题描述】:

我迷路了:std::string 向量的迭代器完美运行,除非在它之前有函数调用 (Z_UB->set() )++。代码如下:

std::vector< std::string >::iterator it = g_SPP.scenarios->getVector().begin();
std::cout << "begin of vector: " << *it << std::endl;
Z_UB->set("s1", "scn2", 350);
it++;
std::cout << "second of vector: " << *it << std::endl;

创建以下输出

begin of vector: scn1

但是,如果我像这样移动函数调用:

std::vector< std::string >::iterator it = g_SPP.scenarios->getVector().begin();
std::cout << "begin of vector: " << *it << std::endl;
it++;
std::cout << "second of vector: " << *it << std::endl;
Z_UB->set("s1", "scn2", 350);

结果如下,这是预期的行为:

begin of vector: scn1
second of vector: scn2

在 Z_UB->set() 函数中,除了调用本身之外什么都没有:

void Parameter::set( std::string _i, std::string _j, float value) {
//int i = indexSets[0]->backIndex(_i);
//int j = indexSets[1]->backIndex(_j);

//data2D[0][0] = value;
}

所以如果我在创建迭代器后调用 Z_UB->set() 函数,访问它会使程序崩溃。关于迭代器,我错过了什么重要的事情吗?

【问题讨论】:

  • 这里没有足够的信息来诊断您的具体问题。迭代器不会“随机”失效,因此问题出在您的代码中,但从这篇文章 where 在您的代码中问题所在并不清楚。 Z_UB 真的是 Parameter 类型吗?是否有任何其他类型可以修改底层vector?在调整vector 的内部存储大小时,vector 迭代器无效。
  • getVector() 是返回对向量的引用还是副本?
  • 它返回一个向量的副本(就我没有在任何地方使用 & 而言)
  • 事实证明,@Bo Persson 有正确的提示!在这一点上,返回向量对象的副本没有意义。它现在就像一个魅力。谢谢大家,我今天学到了很多东西!
  • 我试图回答我自己的问题,但没有足够的声誉,我不能:)。任何人,请回答我的问题,我会将其标记为正确,否则我必须等待 6 小时才能回答我自己的问题。

标签: c++ function iterator call invalidation


【解决方案1】:

几种可能性:

  • 要么您没有很好的可重现示例:也许在您的第一次运行中,您的向量中只有一个元素(它是如何填充的?),并调用了未定义的行为,因为您没有检查 itg_SPP.scenarios-&gt;getVector().end()
  • Z_UB-&gt;set 不会按照您的想法行事。它是一个多态类吗? set 是虚拟的吗? -&gt; 运算符是否重载?
  • 您的应用程序是多线程的,并且另一个线程正在改变您的容器吗?

【讨论】:

  • ->set 函数重载,但我在所有其他版本中添加了一些调试输出。只有我期望运行的那个被调用了。
【解决方案2】:

如果g_SPP 是一个全局 变量,那么在它上面的迭代器将被任何变异操作失效。


更新 -- 这是来自 1998 ISO/ANSI 规范:

如果需要分配,则以下内容会使引用序列元素的所有引用、迭代器和指针无效。如果当前capacity() 小于目标向量大小,则需要分配。

  • void reserve(size_type n)
  • iterator insert(iterator position, const T&amp; x),
  • void insert(iterator position, size_type n, const T&amp; x),
  • void insert(iterator position, InputIterator first, InputIterator last),和

擦除使所有引用、迭代器和指向初始擦除元素位置之后的元素的指针无效。

  • iterator erase(iterator position)
  • iterator erase(iterator first, iterator second)

调整向量大小相当于调用inserterase。根据 23.2.4.2/6:resize(sz, c=value_type()) 与以下效果相同:

if (sz > size())
    insert(end(), sz - size(), c);
else if (sz < size())
    erase(begin() + sz, end());
else
    ;

【讨论】:

  • 很有趣,你能添加一个支持这个的参考吗?
  • 变异是指仅添加和删除元素吗?
【解决方案3】:

std::vector&lt;T&gt;::iterator 在迭代时添加或删除元素,如果向量需要在内部调整自身大小,添加元素时,std::vector&lt;T&gt;::iterator 将失效。

【讨论】:

    【解决方案4】:

    .getVector() 返回向量的副本。将迭代器与完全不同对象的迭代器的端点进行比较是没有意义的。返回引用解决了这个问题。

    @Xeo 还指出了一个更好的解释:当从这样的副本创建迭代器时:

    std::vector< std::string >::iterator it = g_SPP.scenarios->getVector().begin();
    

    副本立即被销毁,从而使刚刚创建的迭代器无效。所以迭代器一开始就不应该返回第一个元素,但我只能猜测这在编译器的实现中隐藏得很深。

    【讨论】:

    • 这也是返回的副本在您创建迭代器的行的末尾被立即销毁,因此迭代器立即失效并且您进入了未定义行为的区域。
    猜你喜欢
    • 1970-01-01
    • 2023-01-13
    • 2016-01-10
    • 2017-10-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多