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
}