1. Core structure
template <class T> struct list_node{<!-- --> //[1] T_data; //[2] list_node *_next; //point to the next node list_node *_prev; // point to the previous node list_node(const T & val = T()) :_data(val), _next(nullptr), _prev(nullptr) {<!-- -->} }; template <class T> class Mylist{<!-- --> typedef list_node<T> Node; //[3] Node *_phead; //[4] //... };
explain
- [1] Use struct to expose member variables to facilitate direct access to node data in the Mylist class.
- [2] The data stored in the node is a template type, so that the linked list can store various types of data including custom types.
- [3] typedef is an alias for the type, which simplifies the code and facilitates later maintenance.
- [4] The Mylist class has only one member variable, which is the pointer to the sentinel node (Mylist is a doubly linked circular list with the head).
Structure diagram:
2. Iterator
2.1 Structure and structure
//list_iterator template <class T, class Ref, class Ptr> struct list_iterator{<!-- --> //[1] typedef list_node<T> Node; typedef list_iterator<T, Ref, Ptr> iterator; Node *_pnode; //[2] list_iterator(Node *pnode) //[3] :_pnode(pnode) {<!-- -->}
explain:
- [1] Use struct to expose member variables to facilitate direct access to the iterator pointer data in the Mylist class.
- [2] The member variable of list_iterator has only one pointer to the node. But encapsulates the method of list iterator:
- dereference returns a reference to data_data
- Arrow returns the address of data_data
- The + + operation realizes the movement of the linked list pointer on the linked list, etc.
- [3] Constructs a list iterator with a pointer to a node.
- [4] The copy construction of list_iterator, assignment overload, and destructor can be generated by default (there is only one pointer inside, and no resource application is involved).
//begin() & end() template <class T> class Mylist{<!-- --> //... public: //iterator typedef list_iterator<T, T &, T*> iterator; typedef list_iterator<T, const T &, const T*> const_iterator; //[2] iterator begin(){<!-- --> return iterator(_phead->_next); //[1] } iterator end(){<!-- --> return iterator(_phead); } const_iterator begin() const{<!-- --> //[3] return const_iterator(_phead->_next); } const_iterator end() const{<!-- --> return const_iterator(_phead); //... }
explain:
- [1] Here, the pointer to the node is used to construct an anonymous object as the return value.
- [2] The latter two template parameters of iterator and const_iterator are different are two different instantiation types:
- The * and -> operations of the iterator class return ordinary references and ordinary pointers.
- The * and -> operations of the const_iterator class return const references and const pointers.
- [3] The const here is used to modify the this pointer, that is, the object calling the function is const Mylist
- A const object returns a const iterator, and the return values of the const iterator’s * and -> operations are read-only const references and const pointers.
- In other words, the node pointed to by the constiterator cannot be modified. (similar to constant pointer const int *ptr)
Structure diagram:
2.2 operator* & operator->
Ref operator*() const{<!-- --> //[3] return _pnode->_data; //[1] } Ptr operator->() const{<!-- --> //[3] return & amp;(_pnode->_data); //[2] } //The following code uses -> to access the members of the object: struct Pos{<!-- --> int_row; int_col; Pos(int row = 0, int col = 0) :_row(row), _col(col) {<!-- -->} }; void Test7(){<!-- --> cout << "Test7: " << endl; Mylist<Pos> mlt; //The data type stored in the linked list is a custom type mlt.push_back(Pos(10,10)); mlt.push_back(Pos(20,20)); for(auto it = mlt.begin(); it != mlt.end(); + + it) {<!-- --> cout << it->_row << ":" << it->_col << endl; //[4] } }
explain:
- [1] The iterator dereference returns a reference to node data_data. After the iterator is dereferenced, you can directly access and modify the node data.
- [2] operator->returns the address of the node data _data of the iterator. It is specially used for custom type _data, you can directly use the iterator plus -> to access the members of _data.
- [3] * and -> operations involve permission issues of ordinary objects and const objects, and need to pass in two template parameters, Ref and Ptr, and generic return value.
- [3] Ordinary objects pass in ordinary references and ordinary pointers, * and -> return references and pointers readable and writable; const objects pass in const references and const pointers, * and -> The references and pointers returned after the operation are readable but not writable.
- [4] There should actually be two arrows here
it->->_row
:- The first arrow calls the operator-> function which returns the address of the _data object.
- The second arrow is used to access the member _row of the _data object.
- To improve readability, an arrow was omitted when designing the C++ syntax. The operation compiler here will do special processing.
2.3 operator== & operator!=
bool operator!=(const iterator & amp;it) const{<!-- --> //[1] return _pnode!=it._pnode; } bool operator==(const iterator & amp;it) const{<!-- --> //[1] return _pnode==it._pnode; }
explain:
- [1] Here we need to distinguish between const iterator objects and const Mylist objects:
- [1] The last const here is used to modify the this pointer, that is, the object of the called function is const iterator.
- const iterator means that its member variable _pnode cannot be changed, that is to say, The pointer of the iterator cannot be changed. (similar to pointer constant int *const ptr)
- const iterator can be dereferenced, -> access member variables, conditional judgment of ==/!=, etc. But operations such as ++ cannot be performed.
2.4 pre + + & amp; post + +
iterator & amp; operator + + (){<!-- --> _pnode = _pnode->_next; //[1] return *this; } iterator operator + + (int){<!-- --> iterator tmp(*this); //[2] _pnode = _pnode->_next; return tmp; }
explain:
- [1] The + + operation of the list iterator is different from the vector iterator (the native pointer directly + +), which needs to access the _next of the node to point the pointer to the next node.
- [2] Post-post ++ needs to copy a temporary iterator to return the previous result of ++. So you need to return by value.
Tip: There are also pre– and post–, you only need to visit the _prev of the node to point the pointer to the previous node.
3. Default member function
3.1 Structure
void empty_initialize(){<!-- --> //[1] _phead = new Node; _phead->_next = _phead; _phead->_prev = _phead; } Mylist(){<!-- --> empty_initialize(); } template <typename InputIterator> Mylist(InputIterator first, InputIterator last){<!-- --> empty_initialize(); //[2] while(first!=last) {<!-- --> push_back(*first); + + first; } }
explain:
- [1] Here, the empty initialization is taken out separately, in order to facilitate multiplexing in the following places
- [2] Empty initialization must be performed before the exchange, otherwise it will crash when accessing wild pointers
3.2 Copy construction & amp; assignment overload
//void swap(Mylist<T> &mlt) void swap(Mylist & amp;mlt){<!-- --> //[1] std::swap(_phead, mlt._phead); } //Mylist<T>(const Mylist<T> &mlt) Mylist(const Mylist & amp;mlt){<!-- --> //[1] empty_initialize(); //Empty initialization must be performed before the exchange, otherwise it will crash when accessing wild pointers Mylist tmp(mlt.begin(), mlt.end()); swap(tmp); } Mylist & operator=(Mylist mlt){<!-- --> swap(mlt); return *this; }
explain:
- [1] When defining a class member function, the type name of the same type can omit the template parameter, but pay attention to:
- Type names of different classes cannot omit template parameters.
- Functions defined outside the class must specify template parameters.
3.3 Destruction
void clear(){<!-- --> iterator it = begin(); while(it != end()) {<!-- --> it = erase(it); } } ~Mylist(){<!-- --> clear(); //clear all data nodes delete _phead; //Release the sentinel bit node }
Note: Be sure to release the sentinel node!
4. Insert and delete
4.1 insert
iterator insert(iterator pos, const T & amp;val){<!-- --> Node *cur = pos._pnode; //[1] Node *prev = cur->_prev; Node *newnode = new Node(val); prev->_next = newnode; newnode->_prev = prev; newnode->_next = cur; cur->_prev = newnode; return iterator(newnode); //[2] }
explain:
- [1] list_iterator uses struct, Mylist class can directly access its members.
- [2] Returns an iterator over the newly inserted node.
4.2 push_back & push_front
void push_back(const T & amp;val){<!-- --> insert(end(), val); } void push_front(const T & amp;val){<!-- --> insert(begin(), val); }
4.3 erase
iterator erase(iterator pos){<!-- --> assert(pos != end()); //[1] Node *cur = pos._pnode; Node *prev = cur->_prev; Node *next = cur->_next; next->_prev = prev; prev->_next = next; delete cur; return iterator(next); //[2] }
explain:
- [1] Sentinel nodes cannot be deleted.
- [2] Returns an iterator to the next node of the deleted node.
4.4 pop_back & pop_front
void pop_back(){<!-- --> assert(_phead->_next != _phead); //[1] erase(--end()); } void pop_front(){<!-- --> assert(_phead->_next != _phead); erase(begin()); }
Note: Before pop, it must be judged as empty!