Understand the iterator in C ++ STL step by step

Source: Internet
Author: User

Understand the iterator in C ++ STL step by step

"Pointer" is no stranger to all C/C ++ programmers. When we access the malloc function in C language and the new function in C ++, we also know that the two functions return a pointer, which points to a "Heap" we applied ". When it comes to "heap", we have to think of "stack" from the perspective of C/C ++ program design, the biggest difference between "stack" and "stack" is that "stack" is automatically allocated and recycled by the system, while "stack" is manually applied by programmers and displayed as release. If the programmer does not show how to release the "heap", it will cause memory leakage and damage to the memory leakage. We all know that, in severe cases, the system will crash.

Since the user of the pointer may accidentally cause memory leakage, how can we make the usage of the pointer safer? From the perspective of C ++ object-oriented analysis, is it possible to encapsulate "Pointers" so that users do not directly access pointers, what about using an encapsulated object to replace pointer operations?

The answer is clearly, "smart pointer" is solving this kind of problem, especially in preventing Memory leakage. The C ++ standard library std provides a "smart pointer class" named "auto_ptr". Let's take a look at the following code:

Void f () {int * p = new int (42); // exception occurred here // delete p ;}

As shown in the code above, if an exception occurs between the two statements, it will cause the memory leakage pointed to by the pointer p. Because an exception occurs before the delete operation, the heap will not be automatically released. However, if you use an auto_ptr object instead of a regular pointer, the memory is automatically released because the compiler can ensure that its destructor is run in advance. In this way, we can change the above Code:

# Include
 
  
// The header file void f () {auto_ptr of auto_ptr
  
   
P (new int (42 ));}
  
 

Through the above analysis, we will have a certain understanding of smart pointers. The iterator is a smart pointer that encapsulates the original pointer and provides operations equivalent to the original pointer, which is convenient and safe.

When it comes to STL, you must immediately think of the six main components: Containers, algorithms, iterators, functions, adapters, and space splitters. This article mainly introduces iterators.The iterator is an important bridge connecting containers and algorithms.Why? Let's give an example to illustrate the cause.

# Include
 
  
# Include // use the find function # include
  
   
Using namespace std; int main () {vector
   
    
Vec; int I; for (I = 0; I <10; I ++) {vec. push_back (I);} if (vec. end ()! = Find (vec. begin (), vec. end (), 7) {cout <"find! "<
    
     

In the above Code, it is worth noting that the find function is used. The function prototype of the find function is template. _ InIt find (_ InIt _ First, _ InIt _ last, const _ Ty & _ val). We can see from the find function prototype that the find function is a function template, the first two parameters of the function are _ InIt, which is short for InputIterator. The last parameter is a reference to any type of variable. While our program is using the find function, the passed real parameters are find (vec. begin (), vec. end (), 7 ), In this way, our parameter iterator associates the algorithm find with the container vector. From this perspective, we can easily understand why the iterator serves as a bridge between the algorithm and the container..

Why is the preceding parameter InputIterator able to accept the real parameter vec. begin? To answer this questionIterator ClassificationSpeaking.

In STL, there are five types of iterators: Input iterator, output iterator, forward iterator, bidirectional iterator, and random access iterator.

Input iterator: Read-only, supports ++, = ,! =;

Output iterator: Write-only, supports ++;

Forward iterator: Read/write, supports ++, = ,! =;

Bidirectional iterator: Read/write, supports ++ ,--,All the standard library containers of C ++ are at least at the level of the two-way iterator..;

Random Access iterator: Read/write, supports ++, --, [n],-n, <, <=,>,> =;

The input iterator and output iterator are the lowest level iterators. The last three iterators are derived from the iterator. Back to the question above, why is the real parameter vec. can begin () be combined with the form parameter _ InIt "virtual reality? Let's take a look at the following code:

# Include
      
       
Using namespace std; class A {}; class A1: public A {}; // class A1 inherits Aclass B {}; void print () // you only need to specify the parameter type {cout <"This is Base class! "<
       
        

From the code above, we can see that in the main function, we call print (A1 (), that is, we can use the derived class object as the real parameter to pass to the function that uses the base class type as the form parameter, therefore, vec. begin () is used as the real parameter, and the input iterator type is used as the form parameter. Both of them can achieve the purpose of combining the real-world and non-error. Therefore, the iterator provides a platform for communication between algorithms and containers.

Next, we will further analyze the iterator from the perspective of the container itself and the algorithm itself.

Container iterators are all self-built,What does it mean? All containers have a built-in iterator, which is implemented by the container designer. Because there are many existing containers, it is impossible to describe the iterators of each container in detail. However, it is certain that the iterators of each container are implemented according to the characteristics of the container, in order to achieve the highest efficiency. Our concerns are:Which operations will invalidate the container iterator??

Is the iterator invalid? That is, the iterator is invalid. The iterator failure means that the element originally pointed to by the iterator does not exist or has moved. If the iterator is not updated, the outdated iterator cannot be used.Root Cause of iterator failureIt modifies the memory status of the container (for example, reload the container to the memory) or moves some elements in the container.

Operations that invalidate the vector iterator

1. Add elements (push_back, insert) to the vector container)

Adding elements to a vector container involves the following two situations:

1) if the whole vector is reloaded after an element is added to the vector, that is, the capacity () of the First and Second vectors return different values, the iterator corresponding to all elements in the container will expire.

2) If the add operation does not cause the whole vector container to load, the iterator pointing to those elements after the newly inserted element will become invalid.

2. Delete (erase, pop_back, clear)

After the deletion operation is performed on the vector, the iterator corresponding to the deleted element and the iterator corresponding to the subsequent element will become invalid.

3. resize operation: adjust the size of the current container

The impact of adjusting the container size on the iterator is discussed as follows:

A. If the size> capacity is adjusted, the whole container will be reloaded and the whole container iterator will become invalid.

B. If the size is adjusted

B1. if the size of the container after adjustment> the size of the container before adjustment, all the iterators of the original vector will not expire;

B2. if the size of the container is adjusted <调整前容器的size,则容器中那些别切掉的元素对应的迭代器都将失效。< p>

4. assign values (v1 = v2 v1.assign (v2 ))

This will invalidate all iterators of the left operand v1. Obviously, the iterator of the right operand v2 will not expire.

5. Switch operation (v1.swap (v2 ))

During the swap operation, neither v1 nor v2 will delete or insert elements, so no elements will be moved in the container. Therefore, all iterators of v1 and v2 will not become invalid.

Operation that invalidates the deque iterator

1. insert operations (push_front, push_back, insert)

The impact of deque insert operations on the iterator is divided into two situations:

A. inserting elements at the beginning or end of the deque container will not be invalid by any iterator;

B. inserting an element at other locations except the beginning and end will invalidate all iterators of the container.

2. delete operation

A. Deleting an element at the beginning and end of deque will only invalidate the iterator corresponding to the deleted element;

B. Deletion operations at any other location will invalidate the entire iterator.

Actions that invalidate the list/map/set iterator

Because the elements in the list/map/set container are connected by pointers, the data structure implemented by the list is a two-way linked list, and the data structure implemented by map/set is a red/black tree, therefore, the insertion and deletion operations of these containers only need to change the pointer direction and do not move the elements in the container. Therefore, when adding elements in the container, no iterator will be invalidated, deleting an element only invalidates the iterator pointing to the deleted element.

Recall the find function. The first two parameters of the find function are of the input iterator type. These two iterators are not a specific iterator of a container, but a general iterator, the iterators in all the standard library containers can be considered as the derived classes of the iterator class corresponding to the form parameters. Next we will analyze the implementation method of the iterator through the implementation code of the iterator in stl.

# Include
         
          
# Include
          
           
// The header file struct detail {} corresponding to ptrdiff_t; // input iterator struct output_iterator_tag {}; // output iterator struct forward_iterator_tag: public input_iterator_tag {}; // forward iterator struct bidirectional_iterator_tag: public forward_iterator_tag {}; // bidirectional iterator struct identifier: public iterator {}; // Random Access iterator // std: iterator, class template of the standard iterator
           
            
Struct iterator // The iterator contains five common attributes: {typedef Category iterator_category; // type of the iterator, one of the five types: typedef T value_type; // type of the element pointed to by the iterator typedef Distance difference_type; // difference between the two iterators typedef Pointer; // The iterator's original pointer typedef Reference; // reference of the element pointed to by the iterator}; // defines iterator_traits, which is used to extract attributes of the iterator. The object of this class should not let the user see the template
            
             
Struct detail {// The following operation is equivalent to a recursive operation, used to recursively extract the correlation value of the original pointer typedef typename Iterator: iterator_category; typedef typename Iterator: value_type; typedef typename Iterator: difference_type; typedef typename Iterator: pointer; typedef typename Iterator: reference ;}; // biased version template for the original pointer
             
              
Struct iterator_traits
              
                {// Equivalent to the recursive termination condition typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t diffrence_type; typedef T * pointer; typedef T & reference ;}; // a special version template designed for pointing to common raw pointers
               
                 Struct iterator_traits
                
                  {Typedef random_access_iterator_tag iterator_category; typedefT value_type; typedef ptrdiff_t diffrence_type; typedef const T * pointer; // typedef const T & reference here }; // define the distance_typetemplate function of the difference type between the two iterators
                 
                   Inline typename iterator_traits
                  
                    : Difference_type * distance_type (const Iterator &) {return static_cast
                   
                     : Difference_type *> (0);} // gets the value_type function template of the iterator.
                    
                      Inline typename iterator_traits
                     
                       : Value_type * value_type (const Iterator &) {return static_cast
                      
                        : Value_type *> (0);} // function for finding the distance between two general iterators _ distance, which is used to call the template for the distance function classification.
                       
                         Inline typename iterator_traits
                        
                          : Difference_type_distance (InputIterator first, InputIterator last, input_iterator_tag) {typename iterator_traits
                         
                           : Difference_type n = 0; while (first! = Last) {++ first; ++ n;} return n;} // function _ distance used to calculate the distance between two random accessors, which is used to call the template for the distance function classification.
                          
                            Inline typename iterator_traits
                           
                             : Difference_type_distance (RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag) {return last-first;} // automatically calls the distance function template
                            
                              Inline typename iterator_traits
                             
                               : Difference_typedistance (InputIterator first, InputIterator last) {typedef typename iterator_traits
                              
                                : Iterator_category category; // you can see from typename that category is a type of return _ distance (first, last, category (); // display the call to the constructor of the category class, returns an object of the class}/***** the following function is used to move the pointer n-bit Method * // The method of moving forward by the General iterator, the template is different from the two-way iterator and the random inverse question iterator.
                               
                                 Inline void _ advance (InputIterator & I, Distance n, input_iterator_tag) {while (n --) {++ I ;}// method template for Bidirectional iterator Movement
                                
                                  Inline void _ advance (BidirectionalIterator & iter, Distance n, bidirectional_iterator_tag) {if (n> = 0) // move backward when n is greater than 0 {while (n --) {+ iter ;}} else // when n is less than 0, move forward {while (n ++) {-- iter ;}}} // define the method template for moving the random access iterator
                                 
                                   Inline void _ advance (RandomAccessIterator & iter, Distance n, random_access_iterator_tag) {iter ++ = n;} // call the advance function template
                                  
                                    Inline void advance (InputIterator & iter, Distance n) {_ advance (I, n, iterator_catetory (iter ));}
                                  
                                 
                                
                               
                              
                             
                            
                           
                          
                         
                        
                       
                      
                     
                    
                   
                  
                 
                
               
              
             
            
           
          
         
From the code above, it is not difficult to find that to implement an iterator, you need to do the following:

1. defines the identifier classes of five types of iterators, which are used to call different functions (that is, overloading). For example, finding the distance between two iterators and the distance function (iter1, iter2, tag ), mobile function advance (iter, n, tag ). These five flag classes are: input_iterator_tag, output_iterator_tag, forward_iterator_tag, bidirectional_iterator_tag, and random_access_iterator_tag.

2. Each iterator class must contain five attributes: iterator_category, value_type, difference_type, pointer, and reference.

3. Define the "attribute juicer" iterator_traits of an iterator to obtain the attribute values in iterator 5.

4. Define the iterator operations:

1) obtain the iterator flag -----> iterator_category (iter );

2) Type of the difference between the two iterators -----> distance_type (iter );

3) obtain the original type of the iterator ---------> value_type (iter );

4) calculate the distance between the two iterators --------------> distance (iter1, iter2, tag );

5) Move the iterator n bits ------------------> advance (iter, n, tag ).

References:

[1] C ++ Primer Chinese Version 4th

[2] STL source code analysis Hou Jie

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.