【问题标题】:How to insert into nested vector without invalidating iterator(s)如何插入嵌套向量而不使迭代器无效
【发布时间】:2009-11-20 02:40:51
【问题描述】:

我有一些布尔表达式要评估和处理。也许使用 Boost 会更好,但我仍在学习 STL,并没有那样做。我现在正在学习迭代器验证,或者视情况而定。有没有办法安全地将新元素插入到下面的这个嵌套向量中?如果您不想看到一个成年人哭泣,请不要建议我重写所有内容 :) 说真的,我也欢迎有关如何以更优雅的方式重写的建议之后我解决了我更直接的问题,我怀疑这是一个无效的迭代器......

...我并不十分关心性能。基于此并阅读其他帖子,也许 std::List 代替 std::vector 会更好,但我需要在嵌套的每个级别吗?

----nested.h

#include <vector>

struct Term {
    uint32_t termNumber;
    std::string content;
    uint32_t importance;
    uint32_t numAppearances;
    uint32_t ContextFlags;
};

struct SubClause {
    std::string typeName;
    std::vector<Term> terms;
    std::string clauseExpression;
};

struct Clause {
    std::vector<SubClause> subClauses;
};

-----nested.cpp

#include <iostream>
#include "nested_container.h"

int main (int argc, char * const argv[]) {
    std::vector< Clause > expression;

    std::vector< Clause >::iterator clauseIter = expression.begin();
    std::vector< Clause >::iterator clauseEnd = expression.end();
    for( ; clauseIter != clauseEnd ; clauseIter++ )
    {
        std::vector< SubClause >::iterator subIter = clauseIter->subClauses.begin();
        std::vector< SubClause >::iterator subEnd = clauseIter->subClauses.end();
        for( ; subIter != subEnd ; subIter++ )
        {
            std::vector< Term >::iterator termIter = subIter->terms.begin();
            std::vector< Term >::iterator termEnd = subIter->terms.end();

            for( ; termIter != termEnd ; termIter++ )
            {

                /* Evaluate SubClause Terms based on some criteria
                 */
                /* if criteria true  */
                if( true/* criteria true? */ )
                {
                    Term newTerm = { };
                    /* fillOutTerm(newTerm) */
                    /* Invalidates the subIter pointer, pretty sure.  Anything else???? */
                    subIter->terms.push_back( newTerm ); //BAD?
                 }
            }

        }
    }

    return 0;
}

【问题讨论】:

标签: c++ vector iterator nested-loops


【解决方案1】:

当您push_back 进入一个向量时,您(可能)使 那个 向量中的迭代器无效,但如果该向量恰好是另一个向量中的项目之一,那么进入另一个向量的迭代器是不受影响。

另一方面,在这种情况下,下标似乎(至少在我看来)生成的代码比迭代器更短、更简单、更清晰:

for (i=0; i<clause.size(); i++) {
    std::vector<term> &terms = clause[i].terms;
    for (j=0; j<terms.size(); j++)
        if (criteria)
            terms.push_back(term(x, y, z));
}

在这种情况下,使用迭代器对我来说是一种净损失。如果足够小心,他们会让您将数据存储在 std::list 中(例如),但这似乎(在这种情况下)并不能弥补额外的长度和阅读困难。迭代器对于可以合理应用于各种容器的通用算法非常有用,但除此之外,它们通常很少或什么都不做。

顺便说一句,vector::size() 可以(至少在理论上)具有线性复杂性,因此对于实际代码,您可能希望将对 size() 的调用从相应的循环中提取出来。

【讨论】:

    【解决方案2】:

    导致向量迭代器无效的三件事:

    • 销毁向量:P
    • push_back 导致重新分配(使所有迭代器无效)
      • 即使没有重新分配也会使 end() 无效
    • 插入或擦除
      • 仅在受影响的项目之后使迭代器失效,但导致重新分配的插入除外,这会使每个迭代器失效

    根据这些定义的其他操作,例如分配(erase+insert)或清除(erase),行为类似。

    Vector在size()需要超过capacity()时重新分配,可以调用reserve()保证capacity()有一定的值。

    因为你在你的内部循环中追加,它需要看起来更像:

    std::vector<Term>::iterator termIter = subIter->terms.begin();
    for (; termIter != terms.end(); ++termIter) {
      if (true/* criteria true? */) {
        Term newTerm = { };
        std::vector<Term>::size_type cur_pos = termIter - subIter->terms.begin();
        subIter->terms.push_back(newTerm);
        termIter = subIter->terms.begin() + cur_pos;
      }
    }
    

    这使用随机访问迭代器的属性来保存位置并恢复它,对于 termIter,无论向量是否必须为 push_back 重新分配。而且由于结束迭代器总是从向量中得到,所以它总是有效的。

    尽管站点上的所有内容并非都适用于 stdlib,但 SGI 在 STL 上有一个 good reference(它与 stdlib 或 stdlib 中的模板不同)及其使用的概念。

    【讨论】:

    • 只是 FWIW,一个 push_back 导致重新分配使 所有 迭代器无效到向量中。
    • 您可能指的是另一个容器。任何可能导致 std::vector 增长的东西都有可能使所有迭代器失效。
    • 杰瑞:是的,我的意思是即使没有重新分配,push_back 也会使 end() 无效;固定。
    【解决方案3】:

    一种选择是创建内部向量的副本,对其执行任何更新,然后交换副本。

    归根结底,它只是构建您的代码,以便在容器(在本例中为向量)被修改后不使用迭代器。

    【讨论】:

      【解决方案4】:

      我认为你的失效太过分了:

      /* fillOutTerm(newTerm) */
      /* Invalidates the subIter pointer, pretty sure.  Anything else???? */
      subIter->terms.push_back( newTerm ); //BAD?
      

      在这个 push_back() 中,只有与 subIter->terms 相关的迭代器才会被添加。 (即 subIter)不受影响,因为您没有更改 subIter 所属的向量。查看您的代码,这可能会使“termIter”和“termEnd”无效。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-06
        • 1970-01-01
        • 1970-01-01
        • 2012-01-05
        • 1970-01-01
        • 2015-07-31
        • 1970-01-01
        • 2020-09-21
        相关资源
        最近更新 更多