Share via


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.

Example

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
}

See Also

Reference

C++ Standard Library Overview