Take the sample code as an example:
Vector <int> V; // Add some elements of FIR (INT I = 0; I <10; ++ I) v. push_back (I); int * my_favorite_element_ptr = & V [3]; cout <"my favorite element =" <(* my_favorite_element_ptr) <Endl; cout <"its address =" <my_favorite_element_ptr <Endl; cout <"adding more elements .. "<Endl; // Add more elements for (INT I = 0; I <100; ++ I) v. push_back (I * 10); cout <"my favorite element =" <(* my_favorite_element_ptr) <Endl; cout <"its address =" <& V [3] <Endl;
What happens to the above Code? We created a vector containing 10 elements and decided to save a pointer to an element with the index position of 3 for some reason. Next, we added some other elements to the vector and tried to reuse the previously saved pointer. Is there any error in this code?
The output of the above Code is as follows:
My favorite element = 3Its address = 0x1001000ccAdding more elements...My favorite element = 3Its address = 0x10010028c
Note that when we add some elements to this vector, the address of the element & V [3] changes. The problem is that when we add some new elements to this vector, the existing elements may be moved to completely different positions.
When we create a vector, it allocates a certain number of elements (usually 16) by default ). Then, when we try to add an element that exceeds the capacity, the vector will allocate a new and larger array to copy the original element from the old location to the new location, then add new elements until the new capacity is used up. The old memory is destroyed and can be used for other purposes.
At the same time, our pointer still points to the old location, and now the memory is destroyed. So what will happen if you continue to use this pointer? If no one is reusing this memory, we will be lucky and will not notice what happened. However, even in this best case, if we write to this location (assign value), it will not modify the value of element V [3] because it is already somewhere else.
If we are not lucky, this memory has been used by others for other purposes, the consequences of this operation may be extremely bad, it is possible to modify the value of an unrelated variable located at this location, it may even cause program crash.
The previous Sample Code involves pointers. if reference is involved, the same thing will happen. For example, we are not writing:
int* my_favorite_element_ptr = &v[3];
It is written:
int& my_favorite_element_ptr = &v[3]
The results are identical. The reason is that the reference is only a "pointer after the referenced ". It knows the address of a variable, but in order to access the memory it points to, it does not need to add an asterisk before the variable. Therefore, although the syntax is different, the result is the same.
Finally, when we use the iterator, the same result will also appear. For example:
vector<int> v;fir(int i=0; i<10; ++i)v.push_back(i);vector<int>::const_iterator old_begin = v.begin();cout<<"Adding more elements..."<<endl;for(int i=0; i<100; ++i)v.push_back(i*10);vector<int>::const_iterator new_begin = v.begin();if(old_begin == new_begin)cout<<"Begin-s are the same."<<endl;elsecout<<"Begin-s are DIFFERENT."<<endl;cout<<"My favorite element = "<<(*my_favorite_element_ptr)<<endl;cout<<"Its address = "<<&v[3]<<endl;
The output result is as follows:
Adding more elements...Begin-s are DIFFERENT.
Therefore, if we save an element pointing to a certain element (which can be any element, not necessarily the element pointed to by begin (), it may expire after the content of the vector is modified, therefore, the internal array of the vector and the corresponding iterator generated by begin () may be moved to another location.
Therefore, any pointer, reference, or iterator that points to an element in the vector before modification should not be used after the element is added to the vector. In fact, this is true for almost all STL containers and all operations that may modify the container length (for example, adding or deleting elements. Some containers, such as hash_set and hash_map, do not officially belong to STL, but they are similar to STL and may be added to STL in the future. When the issues discussed here are involved, they act the same way as STL containers: After the container is modified, the iterator is no longer valid. Although some STL containers retain the iterator that originally points to its elements after adding or deleting elements, the overall spirit of the STL library is to replace another container with one container, the original code is still correct. Therefore, after the STL container or a container similar to STL is modified, it should not be assumed that its iterator is still valid.
Note: In the previous sample code, we modified the container in the thread that accessed the same pointer. If a thread saves a pointer, reference, and iterator, changing the container in another thread will not only cause the same problem, but also lead to more complex situations.
Interestingly, in the above sample code, the index still works when the pointer fails: if an element is marked by an index with a base of 0 (that is, in the first example, for statements such as int index_of_my_favorite_element = 3), this example can be correctly executed. Of course, the index overhead (slower speed) is greater than the pointer, because when accessing the elements corresponding to the index, the vector must perform some operations, that is, the variable address is calculated every time the [] operator is used. Its advantage is that it works, but its disadvantage is that it is only applicable to vector. For all other STL containers, once the container is modified, you must find the iterator pointing to the required element again.
Conclusion: After the container is modified, do not save the pointer, reference, or iterator pointing to the elements in the container.