Discussion on iterator failure

Source: Internet
Author: User

From visual c ++ 2003.net to Visual C ++ 2008, added the Visual C ++ Runtime Library and added assert assertions for detecting incorrect iterator usage. Once an incorrect iterator is found to be used during runtime, A dialog box like ** iterators incompatible will pop up. It is difficult to locate the error because no error is prompted at the position of the program. You can only narrow down the scope and locate the error step by step.

The C ++ Standard describes some member functions that cause the iterator to become invalid. They are all member operations that modify the container status. The following are two examples:

(1) Deleting an element from a container causes the iterator pointing to this element to become invalid.

(2) The container size changes due to the insertion of elements (push and insert). If the original iterator is used, the iterator becomes invalid.

Scenario 1: The original iterator is used after the insert or push method is called.

Program instance:

1,

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

Analysis: In the above program, V. insert (I, 25); after the element is inserted, the container has actually changed, so it will be wrong to attempt to reference the original iterator J later!

Macro definition _ has_iterator_debugging can be used to disable the check assertions when the iterator fails. For example, the following program will not report an error:

Program instance

2:

// Iterator_debugging.cpp
// Compile with:/d_debug/ehs/ 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

While (1)
{}
}

However, the program running results are unsatisfactory:

20
-17891602

Obviously, the second value is the result of the iterator failure!

Scenario 2: The iterator is not initialized.

Program instance
3:
/* compile with /EHsc /MDd */#include <string>using namespace std;int main() {   string::iterator i1, i2;   if (i1 == i2)      ;}

Scenario Analysis 3: apply two different types of iterators to the standard library algorithm. The different types of iterators mentioned here are not at the container type level, the actual situation may be much worse than you think. Even if the container iterators of different instantiated instances of the same container are in the column of different types mentioned above, as shown in the following example,

Program instance

4:

/* Compile with/d_debug/ehs/ MDD */
# Include <algorithm>
# Include <iostream>
# 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
Public:
// Constructor initializes the value to multiply
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.begin (), multvalue <int> (-2 ));

For (vector <int >:: iterator iter = v1.begin (); iter! = V1.end (); ++ ITER)
{
Cout <":" <* ITER <Endl;
}
While (1)
{}
}
Analysis: V1 and V2 are examples of int instantiation by vector. However, iterators incompatible errors are also reported in the for_each algorithm. If the program is changed to the following format, no errors will occur:

Program instance

5:

/* Compile with/d_debug/ehs/ MDD */
# Include <algorithm>
# Include <iostream>
# 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
Public:
// Constructor initializes the value to multiply
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.begin (), multvalue <int> (-2 ));
For_each (v1.begin (), v1.end (), multvalue <int> (-2 ));
For (vector <int >:: iterator iter = v1.begin (); iter! = V1.end (); ++ ITER)
{
Cout <":" <* ITER <Endl;
}
While (1)
{}
}

Scenario 4: If the iterator in the loop body is referenced outside the declared range I, an error is returned.

Program instance

6:

// 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}
However, in this case, the iterators incompatible error is not reported, but I does not declare it.
Scenario 5: The class containing the iterator does not run the Destructor (usually a subclass in inheritance). If the Destructor does not run, it is likely to cause the iterator to access conflicting memory areas, example:

Program instance

7:

/* Compile with:/d_debug/ehs/ 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}

Analysis: we will comment out the virtual destructor of the base first, and there will be memory conflicts in the running results. The solution is to open the virtual destructor of the base, as a result, its subclass runs its own destructor out of scope and releases the iterator resources.

Program instance
8:

/* Compile with:/d_debug/ehs/ 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}

 

Scenario 6: erase deletes the elements in the container and the iterator fails. If you do not assign a value to the iterator again, the iterators incompatibles error may occur.

Program instance

9:

/* UTF-8 */

/* Compile with/d_debug/ehs/ 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 iter = v. begin (); iter! = V. End (); ++ ITER) {If (* iter = 15) v. Erase (ITER);/* error location */}

For (STD: vector <int >:: iterator iter = V. Begin (); iter! = V. End (); ++ ITER) {STD: cout <"* ITER:" <* ITER <STD: Endl;} return 0 ;}

Analysis: In the previous program, after erase deletes an element, it continues the loop without modifying ITER. When compared with end (), assertions appear. The main problem here is that vector can implement erase in any way. It is not guaranteed that after an element of erase, subsequent elements will be moved to the location referenced by this iterator (address ). Of course, this is true in almost all STL implementations, which is why there is no problem after compiling with vc6. However, if we use a list or map instead of a vector, the program will crash without hesitation. The correct method is as follows: An iterator is returned for the erase implementation of all the containers in STL. This iterator points to "the successor element of the currently deleted element, or end ()". Therefore, when deleting an element through erase while traversing all elements of the container, the returned values of erase are assigned to the iteration variable.

Program instance

10:

/* UTF-8 */

/* Compile with/d_debug/ehs/ 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 iter = v. begin (); iter! = V. end ();) // ++ ITER) {If (* iter = 15) {iter = v. erase (ITER) ;}else {++ ITER ;}}

For (STD: vector <int >:: iterator iter = V. Begin (); iter! = V. End (); ++ ITER) {STD: cout <"* ITER:" <* ITER <STD: Endl ;}

Return 0 ;}

Summary: with the change of vs series compilers, a series of software technologies are mature. Container and generic programming are one of them. Although many errors in the past compilation environment will pop up in the early stage, however, this will lead to some facts rather than problems, which will be killed in the initial code development phase, reducing the subsequent workload. Solving such a problem once means a deeper understanding of the bottom layer of the container.

 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.