debug 反復子のサポート

Visual C++ ランタイム ライブラリは、反復子の不正な使用を検出し、実行時にアサートしてダイアログ ボックスを表示します。 反復子のデバッグのサポートを有効にするには、デバッグ バージョンの C++ 標準ライブラリと C ランタイム ライブラリを使ってプログラムをコンパイルする必要があります。 詳しくは、「CRT ライブラリの機能」をご覧ください。 チェックを行う反復子を使う方法については、「チェックを行う反復子」をご覧ください。

C++ 標準では、メンバー関数によってコンテナーに対する反復子が無効になる状況が説明されています。 次に 2 つの例を示します。

  • コンテナーから要素を消去すると、要素に対する反復子が無効になります。

  • プッシュまたは挿入を使って vector のサイズを大きくすると、vector に対する反復子が無効になります。

無効な反復子

デバッグ モードで次のサンプル プログラムをコンパイルすると、実行時にアサートして終了します。

// iterator_debugging_0.cpp
// compile by using /EHsc /MDd
#include <vector>
#include <iostream>

int main() {
   std::vector<int> v {10, 15, 20};
   std::vector<int>::iterator i = v.begin();
   ++i;

   std::vector<int>::iterator j = v.end();
   --j;

   std::cout << *j << '\n';

   v.insert(i,25);

   std::cout << *j << '\n'; // Using an old iterator after an insert
}

_ITERATOR_DEBUG_LEVEL の使用

プリプロセッサ マクロ _ITERATOR_DEBUG_LEVEL を使って、デバッグ ビルドで反復子デバッグ機能を無効にできます。 このプログラムはアサートしませんが、未定義の動作を引き起こします。

// iterator_debugging_1.cpp
// compile by using: /EHsc /MDd
#define _ITERATOR_DEBUG_LEVEL 0
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v {10, 15, 20};

   std::vector<int>::iterator i = v.begin();
   ++i;

   std::vector<int>::iterator j = v.end();
   --j;

   std::cout << *j << '\n';

   v.insert(i,25);

   std::cout << *j << '\n'; // Using an old iterator after an insert
}
20
-572662307

初期化されていない反復子

次に示すように、反復子を初期化する前に反復子を使用しようとすると、アサートも発生します。

// iterator_debugging_2.cpp
// compile by using: /EHsc /MDd
#include <string>
using namespace std;

int main() {
   string::iterator i1, i2;
   if (i1 == i2)
      ;
}

互換性のない反復子

次のコード例では、for_each アルゴリズムに対する 2 つの反復子の間に互換性がないため、アサーションが発生します。 アルゴリズムは、提供された反復子が同じコンテナーを参照しているかどうかを確認します。

// iterator_debugging_3.cpp
// compile by using /EHsc /MDd
#include <algorithm>
#include <vector>
using namespace std;

int main()
{
    vector<int> v1 {10, 20};
    vector<int> v2 {10, 20};

    // The next line asserts because v1 and v2 are
    // incompatible.
    for_each(v1.begin(), v2.end(), [] (int& elem) { elem *= 2; } );
}

この例では、ファンクターの代わりにラムダ式 [] (int& elem) { elem *= 2; } が使われていることに注意してください。 この選択はアサートエラーには影響しませんが、同様のファンクタによって同じエラーが発生しますが、ラムダは短いコードブロックを記述する方法です。 ラムダ式について詳しくは、「ラムダ式」をご覧ください。

スコープ外に出る反復子

また、反復子のデバッグのチェックにより、for ループで宣言されている反復子変数は、for ループのスコープが終了するとスコープ外になります。

// iterator_debugging_4.cpp
// compile by using: /EHsc /MDd
#include <vector>
#include <iostream>
int main() {
   std::vector<int> v {10, 15, 20};

   for (std::vector<int>::iterator i = v.begin(); i != v.end(); ++i)
      ;   // do nothing
   --i;   // C2065
}

デバッグ反復子のデストラクター

反復子のデバッグには重要なデストラクターがあります。 デストラクターが実行されないが、オブジェクトのメモリが解放されると、アクセス違反やデータ破損が発生する可能性があります。 以下に例を示します。

// iterator_debugging_5.cpp
// compile by using: /EHsc /MDd
#include <vector>
struct base {
   // TO FIX: uncomment the next line
   // virtual ~base() {}
};

struct derived : base {
   std::vector<int>::iterator m_iter;
   derived( std::vector<int>::iterator iter ) : m_iter( iter ) {}
   ~derived() {}
};

int main() {
   std::vector<int> vect( 10 );
   base * pb = new derived( vect.begin() );
   delete pb;  // doesn't call ~derived()
   // access violation
}

関連項目

C++ 標準ライブラリの概要