This article mainly introduces the principle and implementation of STL iterator in C + +, which has a certain reference value. Interested partners can refer to it, hoping to help you
1. Introduction to iterators
In order to improve the efficiency of C + + programming, STL (Standard Template Library) provides many containers, including vector, list, map, set, etc. However, some containers (vectors) can access the data in the container through subscript index, but most containers (list, map, set) cannot access the elements in the container in this way. In order to unify the access methods when accessing different containers, STL designs an embedded iterator class for each container during implementation. Different containers have their own exclusive iterators (the exclusive iterators are responsible for implementing the specific details of the corresponding container access elements), and iterators are used to access the data in the container. In addition, containers and general algorithms can be combined through iterators. As long as iterators with different algorithms are given, you can perform the same operations on different containers, such as find lookup function (because iterators provide a unified access mode, which is the benefit of using iterators). Iterators perform some basic operations such as *, - >, + +, = =,! == Overloaded so that it has the ability to traverse complex data structures. Its traversal mechanism depends on the traversed container. The use of all iterators is very similar to that of pointers. Get the head and tail iterators of the container through the begin and end functions. The end iterators are not included in the container. When the iterators returned by begin and end are the same, it means that the container is empty.
STL is mainly composed of container, iterator, algorithm, function object, and memory allocator.
2. Implementation principle of iterator
First, let's look at the implementation idea of iterator in STL:
As can be seen from the above figure, STL realizes external unification by means of type alias; In different containers, the real iterator types of type aliases are different, and the implementation methods of real iterator types for basic operations such as + +, --, *, - > are also different. (PS: iterator well explains the significance of separating interface from implementation)
Now that we know the implementation idea of iterator, how should we implement it if we design a simple iterator of list container ourselves?
1. The list class needs a method to operate the iterator
1.begin/end
2.insert/erase/emplace
2. The list class has an inner class list_iterator
1. A member variable ptr points to an element in the list container
2. The iterator is responsible for reloading + +, --, *, - > and other basic operations
3.list class defines the internal class list_ Type alias for iterator
These are the specific details to consider when implementing a simple iterator for a list container.
3. Simple implementation of iterator
my_list.h (notes for important parts)
// // Created by wengle on 2020-03-14. // #ifndef CPP_PRIMER_MY_LIST_H #define CPP_PRIMER_MY_LIST_H #include <iostream> template<typename T> class node { public: T value; node *next; node() : next(nullptr) {} node(T val, node *p = nullptr) : value(val), next(p) {} }; template<typename T> class my_list { private: node<T> *head; node<T> *tail; int size; private: class list_iterator { private: node<T> *ptr; //Pointer to an element in the list container public: list_iterator(node<T> *p = nullptr) : ptr(p) {} //Overload basic operations such as + +, --, *, - > //Returns a reference to facilitate object modification through * it T &operator*() const { return ptr->value; } node<T> *operator->() const { return ptr; } list_iterator &operator++() { ptr = ptr->next; return *this; } list_iterator operator++(int) { node<T> *tmp = ptr; // This is a list_ Constant pointer to iterator, so * this is a list_iterator object, pre + + has been overloaded ++(*this); return list_iterator(tmp); } bool operator==(const list_iterator &t) const { return t.ptr == this->ptr; } bool operator!=(const list_iterator &t) const { return t.ptr != this->ptr; } }; public: typedef list_iterator iterator; //Type alias my_list() { head = nullptr; tail = nullptr; size = 0; } //Insert elements from the end of the linked list void push_back(const T &value) { if (head == nullptr) { head = new node<T>(value); tail = head; } else { tail->next = new node<T>(value); tail = tail->next; } size++; } //Print linked list elements void print(std::ostream &os = std::cout) const { for (node<T> *ptr = head; ptr != tail->next; ptr = ptr->next) os << ptr->value << std::endl; } public: //Methods of operating iterators //Returns the pointer to the head of the linked list iterator begin() const { return list_iterator(head); } //Returns the pointer at the end of the linked list iterator end() const { return list_iterator(tail->next); } //Other member functions insert / erase / empty }; #endif //CPP_PRIMER_MY_LIST_H
test.cpp
//// Created by wengle on 2020-03-14.//# Include < string > #include "my_list. H" struct student {STD:: String name; int age; student (STD:: String n, int a): name (n), age (a) {} / / overload the output operator friend STD:: ostream & operator < < (STD:: ostream & OS, const student & stu) {OS < < stu. Name < "" < stu. Age; return OS;}}; Int main() {my_list < student > L; l.push_back (student ("Bob", 1)); / / pass the temporary quantity as an argument to the push_back method l.push_back (student ("Allen", 2)); l.push_back (student ("Anna", 3)); l.print(); for (my_list < student >:: iterator it = l.begin(); it! = l.end(); it + +) {STD:: cout < < it < < STD:: endl; * it = student ("wengle") , 18); } return 0;}// // Created by wengle on 2020-03-14. // #include <string> #include "my_list.h" struct student { std::string name; int age; student(std::string n, int a) : name(n), age(a) {} //Overload output operator friend std::ostream &operator<<(std::ostream &os, const student &stu) { os << stu.name << " " << stu.age; return os; } }; int main() { my_list<student> l; l.push_back(student("bob", 1)); //The temporary quantity is passed to the push as an argument_ Back method l.push_back(student("allen", 2)); l.push_back(student("anna", 3)); l.print(); for (my_list<student>::iterator it = l.begin(); it != l.end(); it++) { std::cout << *it << std::endl; *it = student("wengle", 18); } return 0; }
4. Iterator failure
// inserting into a vector #include <iostream> #include <vector> int main () { std::vector<int> myvector (3,100); std::vector<int>::iterator it; it = myvector.begin(); it = myvector.insert ( it , 200 ); myvector.insert (it,200,300); //it = myvector.insert (it,200,300); myvector.insert (it,5,500); //When the program is executed here, the probability will crash for (std::vector<int>::iterator it2=myvector.begin(); it2<myvector.end(); it2++) std::cout << ' ' << *it2; std::cout << '\n'; return 0; }
The above code shows a good example of what iterator failure is? What problems can iterator failure cause?
After executing myvector insert (it,200,300); After this statement, in fact, myvector has applied for a new memory space to store the previously saved data and the data inserted this time. Since the pointer inside the IT iterator still points to the element of the old memory space, once the old memory space is released, execute myvector insert (it,5,500); It will crash (PS: because you are operating a piece of memory that has been released through the pointer of the iterator, it will crash in most cases). Iterator failure means that the pointer inside the iterator is not updated in time and still points to the element of the old memory space.
The figure above shows the implementation of the vector container insert method in the STL source code. When the number of inserted elements exceeds the remaining capacity of the current container, the iterator will fail. This is also MyVector in the test code insert (it,200,300); The reason for inserting 200 elements. In order to simulate the scenario that exceeds the remaining capacity of the current container, if your test environment does not crash, you can set more inserted elements.