Debug Iterator Support
The Visual C++ run-time library detects incorrect iterator use, and asserts and displays a dialog box at run time. To enable debug iterator support, you must use a debug version of a C run-time library to compile your program. For more information, see CRT Library Features. For information about how to use iterators, see Checked Iterators.
The C++ standard describes how member functions might cause iterators to a container to become invalid. Two examples are:
-
Erasing an element from a container causes iterators to the element to become invalid.
-
Increasing the size of a vector (push or insert) causes iterators into the vector to become invalid.
If you compile the following program in debug mode, at run time it will assert and terminate.
/* compile with /EHsc /MDd */ #include <vector> #include <iostream> int main() { std::vector<int> v ; v.push_back(10); v.push_back(15); v.push_back(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 }
You can use the symbol _HAS_ITERATOR_DEBUGGING to turn off the iterator debugging feature in a debug build. The following program does not assert, but still triggers undefined behavior.
Important |
|---|
Use _ITERATOR_DEBUG_LEVEL to control _HAS_ITERATOR_DEBUGGING. For more information, see _ITERATOR_DEBUG_LEVEL. |
// iterator_debugging.cpp // compile with: /EHsc /MDd #define _HAS_ITERATOR_DEBUGGING 0 #include <vector> #include <iostream> int main() { std::vector<int> v ; v.push_back(10); v.push_back(15); v.push_back(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
An assert also occurs if you attempt to use an iterator before it is initialized, as shown here:
/* compile with /EHsc /MDd */ #include <string> using namespace std; int main() { string::iterator i1, i2; if (i1 == i2) ; }
The following code example causes an assertion because the two iterators to the for_each algorithm are incompatible. Algorithms check to determine whether the iterators that are supplied to them are referencing the same container.
/* compile with /EHsc /MDd */ #include <algorithm> #include <vector> using namespace std; int main() { vector<int> v1; vector<int> v2; v1.push_back(10); v1.push_back(20); v2.push_back(10); v2.push_back(20); // The next line will assert because v1 and v2 are // incompatible. for_each(v1.begin(), v2.end(), [] (int& elem) { elem *= 2; } ); }
Notice that this example uses the lambda expression [] (int& elem) { elem *= 2; } instead of a functor. Although this choice has no bearing on the assert failure—a similar functor would cause the same failure—lambdas are a very useful way to accomplish compact function object tasks. For more information about lambda expressions, see Lambda Expressions in C++.
Debug iterator checking also causes an iterator variable that's declared in a for loop to be out of scope when the for loop scope ends.
// debug_iterator.cpp // compile with: /EHsc /MDd #include <vector> #include <iostream> int main() { std::vector<int> v ; v.push_back(10); v.push_back(15); v.push_back(20); for (std::vector<int>::iterator i = v.begin() ; i != v.end(); ++i) ; --i; // C2065 }
Debug iterators have non-trivial destructors. If a destructor does not run, for whatever reason, access violations and data corruption might occur. Consider this example:
/* compile with: /EHsc /MDd */ #include <vector> struct base { // 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 }
Important