2

マルチマップにエントリを挿入するためのこのサンプルコードがあります。指定されたキーの特定のエントリを削除しようとしています。しかし、このコードは無限ループに入ります。誰かがこのコードを手伝ってくれますか?

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main()
{
    multimap<string, string> names;
    string n;

    names.insert(pair<string, string>("Z", "F"));
    names.insert(pair<string, string>("Z", "A"));

    names.insert(pair<string, string>("S", "T"));
    names.insert(pair<string, string>("S", "A"));
    names.insert(pair<string, string>("S", "J"));

    names.insert(pair<string, string>("D", "H"));
    names.insert(pair<string, string>("D", "W"));
    names.insert(pair<string, string>("D", "R"));

    multimap<string, string>::iterator p;


    p = names.find("Z");
    if(p != names.end()) { // found a name
        do {
            cout << n << ", " << p->second;
            cout << endl;
            if (p->second.compare("A") == 0) {
                names.erase(p);
                p++;
            } else {
                p++;
            }
        } while (p != names.upper_bound("Z"));
    }
    else{
        cout << "Name not found.\n";
    }

    p = names.find("Z");
    if(p != names.end()) { // found a name
        do {
            cout << n << ", " << p->second;
            cout << endl;
        } while (p != names.upper_bound("Z"));
    }
    else{
        cout << "Name not found.\n";
    }
    return 0;
}

上記では、キー値「Z」を使用して検索しており、「A」を削除したいと考えています。

4

2 に答える 2

5

multimap::erase消去要素へのイテレータを無効にするため、行

names.erase(p);
p++;

pを消去して無効にし、無効なイテレータをインクリメントしようとします。これを修正するには、一時的なものにコピーpし、pをインクリメントしてから、一時的なイテレータを消去します。

multimap<string, string>::iterator temp = p;
++p;
names.erase(temp);

または、C ++ 11を使用している場合はmultimap::erase、コンテナ内の次のイテレータを返します

p = names.erase(p);

編集:上記は実際には無限ループの原因ではありません。2番目のループではインクリメントしないpので、永久に進みます。ただし、バグを追跡するのが予測不可能で困難になる可能性があるため、修正する必要があります。

于 2012-11-19T00:39:32.973 に答える
2

他の人が言っているように、消去されたばかりの要素を指すイテレータを進めることは、機能することが保証されていません。代わりにできることは、postfix演算子を使用して、消去される前に++消去された要素に続く要素へのイテレーターを取得することです。

names.erase(p++);

C ++ 11では、代わりeraseに、次の要素を指す(または、これend()以上要素がない場合)の戻り値を取得できます。

p = names.erase(p);

また、2番目のループは、カウンターをインクリメントしないため、定義上、無限ループであるとも言われています。

ただし、もう1つ言わなければならないことがあります。要素の範囲の最後の要素に到達したかどうかを確認する方法は、あまり効率的ではありません。upper_boundループのすべての反復を呼び出すと、新しいO(log (n))返されるイテレータは常に同じですが、毎回ツリー検索を行います。

upper_boundループに入って結果を保存する前に実行することで、これを明らかに改善できます。equal_rangeしかし、さらに良いことに、関数を1回実行してから、返された範囲を単純に繰り返すことをお勧めします。

typedef multimap<string,string>::const_iterator mapit;
std::pair<mapit,mapit> range = names.equal_range("Z");
mapit it = range.first;
while (it != range.second)
  if (it->second == "A")
    names.erase(it++);
  else
    ++it;

C ++ 11では、を使用するautoと、これがさらに見栄えが良くなります。

于 2012-11-19T01:04:29.447 に答える