Debug Iterator Support
The Visual C++ runtime library now detects incorrect iterator use and will assert and display a dialog box at run time. To enable debug iterator support, a program must be compiled with a debug version of a C run time library (see C Run-Time Libraries for more information).
See Checked Iterators for more information on using iterators.
The C++ standard describes which member functions 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 container become invalid.
Example
Compile the following program in debug mode and at runtime it will assert and terminate.
/* compile with /D_DEBUG /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
}
The symbol _HAS_ITERATOR_DEBUGGING can be used to turn off the iterator debugging feature in a debug build. The following program will not assert:
// iterator_debugging.cpp
// compile with: /D_DEBUG /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 will also occur if you attempt to use an iterator before it is initialized:
/* compile with /EHsc /MDd */
#include <string>
using namespace std;
int main() {
string::iterator i1, i2;
if (i1 == i2)
;
}
The following code example will cause an assertion because the two iterators to the for_each algorithm are incompatible. All algorithms check to see that the iterators supplied to them are referencing the same container.
/* compile with /D_DEBUG /EHsc /MDd */
#include <algorithm>
#include <vector>
using namespace std;
// The function object multiplies an element by a Factor
template <class Type>
class MultValue
{
private:
Type Factor; // The value to multiply by
public:
// Constructor initializes the value to multiply by
MultValue(const Type& val ) : Factor(val) { }
// The function call for the element to be multiplied
void operator()(Type& elem) const
{
elem *= Factor;
}
};
int main()
{
vector<int> v1;
vector<int> v2;
v1.push_back(10);
v1.push_back(20);
v2.push_back(10);
v2.push_back(20);
// This next line will assert because v1 and v2 are
// incompatible.
for_each(v1.begin(), v2.end(), MultValue<int>(-2));
}
Debug iterator checking also implies /Zc:forScope, such that, an iterator variable declared in a for loop will not be in 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, it could lead to access violations and data corruption. Consider the following example:
/* compile with: /D_DEBUG /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
}