如何插入嵌套向量而不使迭代器失效

时间:2009-11-20 02:40:51

标签: c++ vector iterator nested-loops

我有一些布尔表达式来评估和处理。也许这对Boost来说会更好,但是我还在学习STL而不是那样。我现在正在学习迭代器验证,或者根据具体情况进行INvalidation。有没有办法安全地在下面的嵌套向量中插入新元素?如果你不想看到一个成年男子哭泣,不要建议我重写一切:)严肃地说,我也欢迎有关如何以更优雅的方式重写这个的建议之后我解决了我更直接的问题,我怀疑它是一个无效的迭代器......

......我并不十分关心表现。基于此并阅读其他帖子,或许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;
}

4 个答案:

答案 0 :(得分:1)

有三件事使矢量迭代器无效:

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

根据这些定义的其他操作,例如assign(erase + insert)或clear(erase),表现相似。

当size()需要超过capacity()时,vector会重新分配,你可以调用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恢复它,无论vector是否必须为push_back重新分配。因为结束迭代器总是来自向量,所以它始终有效。

即使站点上的所有内容都不适用于stdlib,SGI在STL上有good reference(它与stdlib不同,或者stdlib中的模板)及其使用的概念。< / p>

答案 1 :(得分: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 :(得分:0)

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

在一天结束时,它只是构建代码,以便在容器(本例中的向量)被修改后不使用迭代器。

答案 3 :(得分:0)

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

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

在这个push_back()中,只有与subIter-&gt;术语相关的迭代器被加入。 (即subIter)不受影响,因为您没有更改subIter所属的向量。查看您的代码,这可能会使'termIter'和'termEnd'失效。