web-dev-qa-db-ja.com

イテレータが有効かどうかを確認する

イテレータ(ベクトル、リスト、デキューなど)が(まだ)逆参照可能、つまり無効化されていないかどうかを確認する方法はありますか?

try-_catchを使用していますが、これを行う直接的な方法はありますか?

例:(機能しません)

list<int> l;
for (i = 1; i<10; i++) {
    l.Push_back(i * 10);
}

itd = l.begin();
itd++;
if (something) {
    l.erase(itd);
}

/* now, in other place.. check if itd points to somewhere meaningful */
if (itd != l.end())
{
    //  blablabla
}
62
huff

私はあなたが「イテレータが有効である」ことを意味すると仮定します、それはコンテナへの変更(例えば、ベクターへ/からの挿入/消去)のために無効化されていません。その場合、いいえ、イテレータが(安全に)逆参照可能かどうかを判断できません。

59
Jason Govig

Jdehaanが言ったように、イテレータが無効化されておらず、コンテナを指している場合、それをcontainer.end()と比較することで確認できます。

ただし、イテレータがsingular-である場合は、初期化されていないか、コンテナの変更操作後に無効になっていることに注意してください(ベクトルの容量を増やすと、ベクトルのイテレータは無効になります)-実行できるのは割り当てのみです。言い換えれば、イテレータが特異かどうかをチェックすることはできません。

std::vector<int>::iterator iter = vec.begin();
vec.resize(vec.capacity() + 1);
// iter is now singular, you may only perform assignment on it,
// there is no way in general to determine whether it is singular or not
25
avakar

移植性のない答え:はい-Visual Studioで

Visual StudioのSTLイテレータには、まさにこれを行う「デバッグ」モードがあります。シップビルドではこれを有効にしたくありません(オーバーヘッドがあります)が、チェックビルドでは便利です。

VC10でそれについてお読みください ここ (このシステムはリリースごとに変更できるため、実際にはバージョンに固有のドキュメントを見つけてください)。

Editまた、追加する必要があります。VisualStudioのデバッグイテレータは、使用するとすぐに爆発するように設計されています(代わりに未定義の動作)。状態の「クエリ」を許可しないようにします。

10
Terry Mahaffey

通常、次のようにend()と異なるかどうかを確認してテストします

if (it != container.end())
{
   // then dereference
}

さらに、ロジックを置き換えるために例外処理を使用することは、設計とパフォーマンスの点で悪いです。あなたの質問は非常に良く、あなたのコードを置き換える価値があることは間違いありません。名前のような例外処理は、まれな予期しない問題にのみ使用されます。

9
jdehaan

イテレータ(ベクトル、リスト、デキューなど)が(まだ)逆参照可能、つまり無効化されていないかどうかを確認する方法はありますか?

いいえ、ありません。代わりに、イテレーターが存在する間、コンテナーへのアクセスを制御する必要があります。例えば:

  • スレッドは、コンテナに対してインスタンス化されたイテレータをまだ使用している間、コンテナを変更しないでください(イテレータを無効化)。

  • スレッドの反復中に他のスレッドがコンテナを変更するリスクがある場合、このシナリオをスレッドセーフにするために、スレッドはコンテナ上の何らかの種類のロックを取得する必要があります(そのため、他のスレッドがコンテナを変更できないようにしますイテレータを使用しています)

例外をキャッチするなどの回避策は機能しません。

これは、「ポインターが有効かどうかをテスト/検出できますか?」という一般的な問題の特定のインスタンスです。通常、「いいえ、テストできません。代わりにすべてのメモリ割り当てを管理する必要があります」 know与えられたポインターがまだ有効かどうかを確認するための削除」。

7
ChrisW

イテレータが「境界外」である場合、試行および捕捉は安全ではなく、安全ではありません。

alemjerusが言っているように、イテレータは常に間接参照できます。どんないことが下にあるとしても。メモリの他の領域を反復処理し、他のオブジェクトを保持する可能性のある他の領域に書き込むことは非常に可能です。私はコードを見て、変数が特定の理由で変化するのを見ていました。これは、検出が非常に難しいバグです。

また、要素の挿入と削除により、all参照、ポインター、反復子が無効になる可能性があることを覚えておくのが賢明です。

私の最善のアドバイスは、イテレーターを管理し、「エンド」イテレーターを常に手元に置いて、いわば「行末」にいるかどうかをテストできるようにすることです。

3
daramarak

イテレータが逆参照可能かどうかを確認する方法はありますか

はい、gccで コンテナのデバッグ GNU extensions。For std::listを使用できます__gnu_debug::list代わりに。次のコードは、無効な反復子の使用が試行されるとすぐに中止されます。コンテナをデバッグすると余分なオーバーヘッドが発生するため、デバッグ時のみに使用することを想定しています。

#include <debug/list>

int main() {
  __gnu_debug::list<int> l;
  for (int i = 1; i < 10; i++) {
    l.Push_back(i * 10);
  }

  auto itd = l.begin();
  itd++;
  l.erase(itd);

  /* now, in other place.. check if itd points to somewhere meaningful */
  if (itd != l.end()) {
    //  blablabla
  }
}

$ ./a.out 
/usr/include/c++/7/debug/safe_iterator.h:552:
Error: attempt to compare a singular iterator to a past-the-end iterator.

Objects involved in the operation:
    iterator "lhs" @ 0x0x7ffda4c57fc0 {
      type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
      state = singular;
      references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0
    }
    iterator "rhs" @ 0x0x7ffda4c580c0 {
      type = __gnu_debug::_Safe_iterator<std::_List_iterator<int>, std::__debug::list<int, std::allocator<int> > > (mutable iterator);
      state = past-the-end;
      references sequence with type 'std::__debug::list<int, std::allocator<int> >' @ 0x0x7ffda4c57ff0
    }
Aborted (core dumped)
1
ks1322

一部のSTLコンテナでは、イテレータの現在の値を消去すると、現在のイテレータが無効になります。これは、消去操作によってコンテナの内部メモリ構造が変更され、既存のイテレータポイントのインクリメント演算子が未定義の場所を指すために発生します。

次の操作を行うと、イテレーターは消去関数に渡される前に増分されます。

if (something) l.erase(itd++);

1
lava

Stdコンテナーの消去機能のパラメーターのタイプ(質問でリストしたとおり、つまり、ベクター、リスト、デキューからのものかどうか)は、常にですこのコンテナの反復子only

この関数は、最初に指定されたイテレータを使用して、このイテレータが指す要素、およびそれに続く要素さえもコンテナから除外します。一部のコンテナは、1つのイテレータに対して1つの要素のみを消去し、他のコンテナは、コンテナの終わりまで1つのイテレータが続くすべての要素(このイテレータが指す要素を含む)を消去します。消去関数が2つのイテレータを受け取ると、各イテレータが指す2つの要素がコンテナから消去され、それらの間のすべての要素もコンテナから消去されますが、ポイントはthatstdコンテナのerase関数に渡されるすべてのイテレータは無効になります! Also

コンテナから消去された要素を指していた各イテレータは無効になりますが、コンテナの終わりを通過しません!

これは、コンテナから消去された要素を指すイテレータをcontainer.end()と比較できないことを意味します。この反復子は無効であるため、逆参照できません。つまり、*演算子も->演算子も使用できません。また、インクリメントもできません。つまり、++演算子を使用できません。また、デクリメントもできません。オペレーター。

これも比較できない!!! I.E. ==演算子も!=演算子も使用できません

実際、stdイテレータで宣言および定義されている演算子は使用できません。 nullポインターのように、このイテレーターでは何もできません。

無効なイテレータで何かをすると、プログラムがすぐに停止し、プログラムがクラッシュし、アサーションダイアログウィンドウが表示されます。選択したオプション、クリックしたボタンに関係なく、プログラムを続行する方法はありません。 [中止]ボタンをクリックするだけで、プログラムとプロセスを終了できます。

無効なイテレータを使用して、コンテナの先頭に設定するか、単に無視しない限り、他に何もしません。

ただし、イテレータの処理を決定する前に、使用しているコンテナの消去機能を呼び出す場合、まずイテレータが無効かどうかを知る必要があります。

特定のイテレータが無効かどうかをチェック、テスト、認識、およびtrueを返す関数を自分で作成しました。 memcpy関数を使用して、オブジェクト、アイテム、構造、クラスなどの状態を取得できます。もちろん、最初に常にmemset関数を使用して、新しいバッファー、構造、クラス、またはオブジェクトまたはアイテムをクリアまたは空にします。 :

bool IsNull(list<int>::iterator& i) //In your example, you have used list<int>, but if your container is not list, then you have to change this parameter to the type of the container you are using, if it is either a vector or deque, and also the type of the element inside the container if necessary.
{
    byte buffer[sizeof(i)];
    memset(buffer, 0, sizeof(i));
    memcpy(buffer, &i, sizeof(i));
    return *buffer == 0; //I found that the size of any iterator is 12 bytes long. I also found that if the first byte of the iterator that I copy to the buffer is zero, then the iterator is invalid. Otherwise it is valid. I like to call invalid iterators also as "null iterators".
}

そこに投稿する前にこの関数をテストしましたが、この関数が機能していることがわかりました。

私はあなたの質問に完全に答え、またあなたを大いに助けてくれたことを非常に願っています!

0
user2133061