Unterstützung für Iteratordebugging

Mit der Laufzeitbibliothek von Visual C++ wird eine nicht ordnungsgemäße Verwendung eines Iterators erkannt, zur Laufzeit eine Assertanweisung ausgeführt und ein entsprechendes Dialogfeld angezeigt. Wenn Sie die Unterstützung für das Iteratordebugging aktivieren möchten, kompilieren Sie Ihr Programm mit Debugversionen der C++-Standardbibliothek und der C-Laufzeitbibliothek. Weitere Informationen finden Sie unter CRT Library Features (CRT-Bibliotheksfunktionen). Informationen zur Verwendung von überprüften Iteratoren finden Sie unter Checked Iterators (Überprüfte Iteratoren).

In der C++-Standardbibliothek wird beschrieben, auf welche Weise Memberfunktionen dazu führen können, dass Iteratoren für einen Container ungültig werden. Zwei Beispiele:

  • Durch das Löschen eines Elements in einem Container werden Iteratoren für das Element ungültig.

  • Durch das Vergrößern eines vector-Containers mittels „push“ oder „insert“ werden Iteratoren im vector-Container ungültig.

Ungültige Iteratoren

Wird dieses Beispielprogramm im Debugmodus kompiliert, wird eine Assertanweisung ausgeführt und das Programm wird beendet.

// 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
}

Verwenden von _ITERATOR_DEBUG_LEVEL

Mit dem Präprozessormakro _ITERATOR_DEBUG_LEVEL können Sie die Funktion für das Iteratordebugging in einem Debugbuild deaktivieren. Dieses Programm bestätigt nicht, löst aber trotzdem nicht definiertes Verhalten aus.

// 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

Nicht initialisierte Iteratoren

Eine Assertion tritt auch auf, wenn Sie versuchen, einen Iterator vor der Initialisierung zu verwenden, wie hier gezeigt:

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

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

Inkompatible Iteratoren

Durch das folgende Codebeispiel wird eine Assertion verursacht, da die beiden Iteratoren für den for_each-Algorithmus nicht kompatibel sind. Für Algorithmen wird überprüft, ob von den bereitgestellten Iteratoren auf denselben Container verwiesen wird.

// 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; } );
}

In diesem Beispiel wird anstelle eines functor-Objekts der Lambdaausdruck [] (int& elem) { elem *= 2; } verwendet. Obwohl diese Wahl keinen Einfluss auf den Assert-Fehler hat – ein ähnlicher Functor würde denselben Fehler verursachen – Lambdas sind eine Möglichkeit, einen kurzen Codeblock zu schreiben. Weitere Informationen zu Lambdaausdrücken finden Sie unter Lambdaausdrücke.

Iteratoren, die außerhalb des Gültigkeitsbereichs gehen

Debugiteratorüberprüfungen führen darüber hinaus auch dazu, dass sich eine in einer for-Schleife deklarierte Iteratorvariable am Ende des for-Schleifenbereichs außerhalb des Bereichs befindet.

// 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
}

Destruktoren für Debug-Iteratoren

Debugiteratoren enthalten nicht triviale Destruktoren. Wenn ein Destruktor nicht ausgeführt wird, der Arbeitsspeicher des Objekts jedoch freigegeben wird, können Zugriffsverletzungen und Datenbeschädigungen auftreten. Betrachten Sie das folgende Beispiel:

// 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
}

Siehe auch

Übersicht über die C++-Standardbibliothek