Generic programming learning, writing an iterator similar to a simple list in STL Libraries

Keywords: Programming github

Generic programming learning, writing an iterator similar to a simple list in STL Libraries

Preface

Recently, in the study of STL source code and various implementation details in stl, beginners will inevitably imitate, the following will write their own simple list container iterator.
First, when we start writing List iterators, we should first understand what List we are going to write and what functions its iterators will perform. (Learning books C++STL Basic Applications, STL Source Code Analysis and Generic Thinking)

list

Compared with vector, list has the advantage that every time a data is inserted/deleted, it reapply/release a space and absolutely grasp memory accurately (vector is to apply for an area, beyond which it will "reconfigure twice the area, move elements, release the original space"); in addition, for the movement of elements, list is always a constant time.

iterator

Each container should have a corresponding iterator, which shares a specific algorithm through the iterator (and then uses display() function as an example). Iterator is the medium between algorithm and container (display(iterator it1,iterator it2). The generation of iterator thinking is the inevitable result of the development of generic algorithm.

Simple display() function

template<class T>
void display(T start, T end) {
	cout << endl;
	for (T i = start; i != end; i++) {
		cout << *i << endl;
	}
	cout << endl;
}

My goal is to enable the list container I wrote to use this display() function.

To write

list

The basic list needs that we want: the basic node struct Node (with * head and * prev pointing to the next and previous nodes, respectively)

//	  head->Node1->Node2->......Noden->tail
//	  next ->  next  ->  next  ->......  next  ->tail->NULL
//NULL<-  prev<-  prev  <- prev   <-...... prev  
template<class T>
	struct  Node
	{
		Node* next;
		Node* prev;
		T data;
		Node(T s = 0, Node* n = NULL, Node* p = NULL) :data(s), next(n), prev(p) {};
		//Node's Constructive Function
	};

Now that Node has been identified, it begins to write the class list.

template<class T>
class list {
public:
	struct  Node
	{
		//......
	};
	//iterator
	class iterator 
	{
		//......
	};
	//_ iterator of list
private:
	Node * head,*tail;         //The list we wrote contains the first and last pointers
	int _size;
	void createlist() {
		_size = 0;
		head = new Node();
		tail = new Node();
		head->next = tail;
		tail->prev = head;
	}
public:
	list() {
		createlist();
	}
	~list() {
		while (!empty()) {
			pop_front();
		}
		delete head;
		delete tail;
	}
	bool empty()const { return _size == 0; };
	iterator begin() {
		return iterator(head->next);
	}
	iterator end() {
		return iterator(tail);
	}
	iterator insert(iterator pos, const T& value) {  
	//Const T&value: const prevents references from being modified. Reference passing is equivalent to passing pointers, 8 bytes. 
	//Value passing calls the replication constructor, which takes up a lot of memory.
		Node* p = new Node(value, pos.cur, pos.cur->prev);
		_size++;
		p->prev->next = p;
		pos.cur->prev = p;
		return iterator(p);
		// The Writing of Big Man
		//return iterator(p->prev = p->prev->next = new Node(val, p, p->prev));
	}
	iterator erase(iterator pos) {
		Node* p = pos.cur;
		iterator newite(p->next);
		p->prev->next = p->next;
		p->next->prev = p->prev;
		delete p;
		_size--;
		return newite;
	}
	void push_back(const T& value) {
		insert(end(), value);
	}
	void push_front(const T& value) {
		insert(begin(), value);
	}
	void pop_front() {
		erase(begin());
	}
	void pop_back() {
		erase(end());
	}
};

In this way, our list contains the basic functions of push_back,push_front,pop_back,pop_front,insert,erase, and constructor and destructor.

Compiling iterator of list

template<class T>
class list {
public:
	struct  Node
	{
		//... Node's implementation
	};
	//iterator
	class iterator {
	private:
		Node* cur; //Pointer, pointing to the node of link
	public:
		friend class list;   
		//Important! Declare list as a friend of iterator, iterator can access the private of list
		explicit iterator(Node* p=0) {cur = p; } 
		//Prevent the occurrence of implicit transformations that should not be allowed through transformation constructors.
		//Constructors declared as explicit cannot be used in implicit transformations
		bool operator ==(iterator& x) { return (*this).cur == x.cur; }
		bool operator !=(iterator& x) { return (*this).cur != x.cur; }
		iterator& operator++() {  //Prefix + +
			cur = cur->next;
			return *this;
		}
		iterator operator++(int) { //Suffix + +
			iterator temp = *this;
			++(*this);
			return temp;
		}
		iterator& operator--() {
			cur = cur->prev;
			return *this;
		}
		iterator operator--(int) {
			iterator temp = *this;
			--(*this);
			return temp;
		}
		Node* operator->() {
			return cur;
		}
		T operator*() { return cur->data; }
		//If we don't write this one, we can use Writing Overload Global
		//ostream operator<<(ostream& os,const iterator& it)
		//To implement "cout < * iterator;" in display().
	};
	//_ iterator of list
	//......
	//The implementation of list
};

Because the display() function uses ++,! =,->, so you need to write the symbol overload of the iterator.

T operator*() { return cur->data; }

If we don't write this one, we can use Writing Overload Global

ostream operator<<(ostream& os,const iterator& it){ //Implementation details}

To implement the display()“

cout<<*iterator;

main() function and operation results

#include"_list.h"
#include<iostream>
using namespace std;
int main() {
	list<int>a;
	for (int i = 0; i < 5; i++) {
		a.push_back(i);
	}
	list<int>::iterator itb = a.begin();
	list<int>::iterator ite = a.end();
	display(itb, ite); // 0 1 2 3 4

	a.insert(ite, 5);
	display(itb, ite); //0 1 2 3 4 5 

	cout << *itb<<endl; // 0
	cout<<*(++itb)<<endl;//1
	cout << *(--ite) << endl<<endl;//5

	a.pop_back();
	display(a.begin(), a.end());// 0 1 2 3 4

	
	system("pause");
	return 0;
}

So far, the resume list and its iterators have been written.
STL list container class clear,remove,unique,splice,merge,reverse,sort and other element operations are retained after implementation.
github Personal Blog

Posted by rtconner on Tue, 07 May 2019 06:30:40 -0700