【问题标题】:C++:cannot seek vector iterator before beginC++:在开始之前不能寻找向量迭代器
【发布时间】:2020-02-08 04:36:15
【问题描述】:

我在做一个uva问题uva 10935 throwing cards away,我的代码如下。当我运行它时,它说引发了未处理的异常:读取访问冲突,并且它还显示“在开始之前无法查找向量迭代器”,我不知道我的代码中的问题在哪里:

#include<vector>
#include<iostream>
using namespace std;
int n;
vector<int> out;


int main() {
    freopen("data.txt", "r", stdin);
    while (scanf("%d", &n) == 1 && n) {
        vector<int> cards;
        for (int i = 1; i <= n; i++)cards.push_back(i);
        vector<int>::iterator it = cards.begin();
        vector<int>::iterator end = cards.end();
        while (it != (end-1)) {
            out.push_back(*it);
            it++;
            cards.push_back(*it);
            it++;
            end++;
        }
        cout << "Discarded cards: ";
        for (int j = 0; j < out.size(); j++) {
            if(j!=(out.size()-1))cout << out[j] << ", ";
            else cout << out[j] << endl;

        }
        cout << "Remaining card: " << *it << endl;
    }
    return 0;
}

【问题讨论】:

    标签: c++ vector iterator


    【解决方案1】:

    这里的问题仍然存在于 while() 循环中,您在其中 在向量中推送新元素,而 保持它是旧的“结束”地址作为终端参考。

    当向量推送一个新元素时,它可能需要将整个数组重新分配到一个新位置。在这种情况下,您所持有的“end”引用将随着您线性增加它而变得过时,但推动之后的整个向量可能已经转移到其他地方了。

    我在您的代码中添加了一些调试行,因此您可以看到这是如何发生的。 只需运行输入值为 4 的代码。你会看到“结束”值可能不再与向量的重新分配地址相关(如果发生重新分配,它基本上取决于系统来决定)。

    #include<vector>
    #include<iostream>
    using namespace std;
    int n;
    vector<int> out;
    
    void printVectorElementAddresses(vector<int> &v){
        cout<<"==== Vector Element Addresses ====\n";
        for(auto itr = v.begin(); itr != v.end(); itr++)
        {
            cout<<&(*itr);
            if(itr == v.end()-1){
                cout<<endl;
            }
            else{
                cout<<" ";
            }
        }
        cout<<endl;
    }
    
    void printIteratorAddressWithTag(char* tag, vector<int> :: iterator & it, bool printNewLine){
        cout<<tag<<&*it<<"; ";
        if(printNewLine){
            cout<<endl;
        }
    }
    
    int main() {
    //    freopen("data.txt", "r", stdin);
        while (scanf("%d", &n) == 1 && n) {
            vector<int> cards;
            for (int i = 1; i <= n; i++)cards.push_back(i);
            vector<int>::iterator it = cards.begin();
            vector<int>::iterator end = cards.end();
    
            //print vector addresses after initial pushes are done
            printVectorElementAddresses(cards);
    
            while (it != (end-1)) {
                printIteratorAddressWithTag("it initial = ", it, false);
                out.push_back(*it);
                it++;
    
                printIteratorAddressWithTag("it after first increment = ", it, false);
                cards.push_back(*it);
                it++;
    
                printIteratorAddressWithTag("it after second increment = ", it, true);
    
                printIteratorAddressWithTag("end initially in loop = ", end, false);
                end++;
    
                printIteratorAddressWithTag("end after increment = ", end, true);
    
                cout<<"Vector Addresses after a new push"<<endl;
                printVectorElementAddresses(cards);
            }
            cout << "Discarded cards: ";
            for (int j = 0; j < out.size(); j++) {
                if(j!=(out.size()-1))cout << out[j] << ", ";
                else cout << out[j] << endl;
    
            }
            cout << "Remaining card: " << *it << endl;
        }
        return 0;
    }
    

    只需更改 while 循环中的逻辑,以便在推送发生后跟踪旧的“结束”引用。如果其他逻辑没问题,它应该可以工作。

    【讨论】:

      【解决方案2】:

      我认为 end++ 和其他迭代器存在问题。

      如果向量卡增长,它的内存位置可能会改变,end++ 将指向一个无效的位置。 it++也会发生类似的情况。

      我建议您改用列表:

      #include<bits/stdc++.h>
      using namespace std;
      int n;
      
      int main() {
          //freopen("a.in", "r", stdin);
      
          while (scanf("%d", &n) == 1 && n) {
      
              list<int> out;
      
              list<int> cards;
              for (int i = 1; i <= n; i++)
                cards.push_back(i);
              list<int>::iterator it = cards.begin();
      
              list<int>::iterator end = cards.end();
              end--;
              while (it != (end)) {
                  out.push_back(*it);
                  it++;
                  cards.push_back(*it);
                  it++;
                  end++;
              }
              cout << "Discarded cards: ";
              list<int>::iterator it2 = out.begin();
              for (int j = 0; j < out.size(); j++, it2++) {
                  if(j !=(out.size()-1))
                     cout << *it2 << ", ";
                  else
                    cout << *it2 << endl;
              }
              cout << "Remaining card: " << *it << endl;
          }
          return 0;
      }
      
      

      注意:我还没有阅读 UVA 问题。

      【讨论】:

      • 您要查找的术语是迭代器失效。见std::vector::push_back。真正的解决方法是停止缓存end,而是在修改矢量大小时使用cards.end()。而std::list 的性能远比std::vector 差,因为它不是缓存友好的数据结构
      • @alterigel 好的,我部分同意。 cards.end() 是我首先提出的。然后我运行程序仍然崩溃,然后我看到问题仍然存在++。所以我改变了我的答案:为了不改变很多原始代码,我提出了列表。该代码具有相同的异步时间复杂度,并且是(通常)重要的是获得在线评委的接受
      • 我想你错过了迭代器 it 也可能被 push_back 无效
      • @alterigel 好吧,我在之前的评论中说过我错过了这一点,然后我将答案更改为使用列表。列表不存在此问题。
      猜你喜欢
      • 2021-02-07
      • 2021-12-26
      • 2023-03-10
      • 2014-05-27
      • 2012-02-09
      • 1970-01-01
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多