1

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

...我并不十分关心性能。基于此并阅读其他帖子,也许 a std::Listin place ofstd::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

4 回答 4

1

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

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

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

当 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,无论向量是否必须为 push_back 重新分配。而且由于结束迭代器总是从向量中得到,所以它总是有效的。

尽管站点上的所有内容并非都适用于 stdlib,但 SGI对 STL(它与 stdlib 或 stdlib 中的模板不同)及其使用的概念有很好的参考。

于 2009-11-20T02:51:58.270 回答
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() 的调用从各自的循环中提取出来。

于 2009-11-20T03:42:45.970 回答
0

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

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

于 2009-11-20T03:06:30.023 回答
0

我认为您将无效化过分了:

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

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

于 2009-11-20T04:08:20.333 回答