Simulation implementation of list (imitating STL)

Table of contents

1. Preparation before simulation implementation

1. Understanding the list structure

2. The implementation of iterators is different

3. How to implement the required functions

2. Node class implementation

3. Iterator implementation

1. Problems before implementation

2. Member variables and constructors of the _list_iterator class

3.* and -> operator overloading

4. Implementation of pre++ and post++

5. Pre-- and post--

6.== and != operator overloading

4. Implementation of list class

1.list class and constructor

2. Destruction

3.Copy constructor

4. Assignment operator overloading

5.Iterator

6.insert()

7.erase()

8.clear()

9. Delete the head plug and delete the tail plug.

10. Short call

5. Complete code


1. Preparation before simulation implementation

1. Understanding the list structure

1. The underlying structure of STLe's list is actually a bidirectional circular linked list with the head node. Each element in the bidirectional linked list is stored in an independent node that is not related to each other. In the node, a pointer points to its previous element and next element.

 

2. The implementation of iterators is different

Inserting an element into the list will not cause the iterator to become invalid. When an element is deleted, it will only cause the current iterator to become invalid, and other iterators will not be affected. The iterator of list is a custom type that encapsulates native pointers and simulates the behavior of pointers. So why not use native pointers directly like vector? Because each node of the list is not continuous in the physical structure, if you directly ++ the node, what you get is not the next node pointer.

3. How to implement the required functions

Here we can use three classes to simulate the implementation. The node class and list iterator class are encapsulated with struct, because we do not need to restrict access to them, and the list class is encapsulated with class class.

 

2. Node class implementation

The node class implementation is relatively simple, but we need to pay attention to:

(1) Templates are needed to facilitate the use of different types in the future;

(2) Node member variables include node values, pointers to the previous node, and pointers to the next node;

(3) No destructor is required (no additional application space);

(4) Use struct to implement without any restrictions on access.

template<class T>                 //list的结点结构,对访问不做限制,所以用struct
	struct list_node
	{
		list_node<T>* _next;       //成员变量
		list_node<T>* _prev;
		T _data;

		list_node(const T& x = T())       //构造函数
			:_next(nullptr)
			, _prev(nullptr)
			, _data(x)
		{}
	};

3. Iterator implementation

The iterator implementation of list is the most exciting part of the entire implementation process, especially the multiple parameters in the template. It can be said that the rules are used to the extreme, and there are many knowledge points involved. We have to lament the strength of the STL boss. .

1. Problems before implementation

Iterators are divided into ordinary iterators and const iterators. Since the previous iterators were directly used native pointers, even if the two iterators were implemented separately, the amount of code would be very small. But for the _list_iterator class to implement the ordinary _list_iterator and the const version of _list_const_iterator, because some functions in the two classes are divided into ordinary functions and const functions (such as begin() and end()), and there is no other the difference. Because most of the codes of these two classes are similar, how to solve it?

Looking at the STL source code, we can find that the following method is very good:

The iterator template uses three parameters

template<class T, class Ref, class Ptr>

When list is instantiated, two classes are instantiated through different parameters, one is ordinary T, T&, T* without const, and the other is T, const T&, const T* with const, where Ref is a reference. , Ptr is a pointer, and this class template instantiates the following two class templates:

typedef __list_iterator<T, T&, T*> iterator;   //非const 迭代器
                        
typedef __list_iterator<T, const T&, const T*> const_iterator;     //const迭代器

In this way, we solve the problem of code redundancy (the boss is indeed a boss).

2. Member variables and constructors of the _list_iterator class

The member variable is only node, and the constructor is responsible for initializing the node.

template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> node;                                         //
		typedef __list_iterator<T, Ref, Ptr> self;
		node* _node;                //成员变量

		__list_iterator(node* n)      //构造函数
			:_node(n)
		{}
    };

3.* and -> operator overloading

Ref operator*()
{
return _node->_data;     //解引用之后,我们需要得到拷贝的值,所以返回引用
}

Ptr operator->()
{
return &_node->_data;    //结构体指针解引用,返回结点指针
}

Note: It->_data, (It is an iterator) We usually use it like this without any problem, but we need to know that one -> is actually omitted here, It-> is actually It.operator->(), and its return value is For the _data* type, we also need It.operator->()->_data, but the compiler optimizes an arrow for readability.

4. Implementation of pre++ and post++

self& operator++()            //都需要用到自身,所以是self
{
_node = _node->_next;
return *this;
}

self operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}

5. Pre-- and post--

        self& operator--()
		{
			_node = _node->_prev;

			return *this;
		}

		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

6.== and != operator overloading


		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}

4. Implementation of list class

1.list class and constructor

template<class T>
	class list                           //list类
	{
		typedef list_node<T> node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;                     //非const 迭代器
		typedef __list_iterator<T, const T&, const T*> const_iterator;       //const迭代器

		//typedef __list_const_iterator<T> const_iterator;
   list()
		{
			_head = new node;                    //默认构造函数
			_head->_next = _head;
			_head->_prev = _head;
		}
   private:
		node* _head;  
    };

2. Destruction

Reuse the following functions

        ~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				//it = erase(it);
				erase(it++);
			}
		}

3.Copy constructor

        void empty_init()          //空初始化
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}
		list(const list<T>& lt)
		{
		empty_init();
		for (auto e : lt)
		{
		push_back(e);
		}
		}

4. Assignment operator overloading

(1) Modern assignment operator overloading


		void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}

        list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}
	

When performing assignment operator overloading, the copy constructor will be called to perform a deep copy , and then the pointers of the two linked list head nodes will be exchanged , and the resources to be released by this will be handed over to the temporary object. The temporary object will be replaced by this when it leaves the function scope of swap. The resources to be released will also be released at the same time.

5.Iterator

        iterator begin()
		{
			//iterator it(_head->_next);
			//return it;
			return iterator(_head->_next);     //匿名对象
		}

		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

		iterator end()
		{
			return iterator(_head);
		}

		const_iterator end() const
		{
			//iterator it(_head->_next);
			//return it;
			return const_iterator(_head);
		}

6.insert()

First construct a node and then insert it

        void insert(iterator pos, const T& x)
		{
			node* cur = pos._node;
			node* prev = cur->_prev;

			node* new_node = new node(x);

			prev->_next = new_node;
			new_node->_prev = prev;
			new_node->_next = cur;
			cur->_prev = new_node;
		}

7.erase()

After deletion, you need to return to the next node at the deleted position

        iterator erase(iterator pos)
		{
			assert(pos != end());

			node* prev = pos._node->_prev;
			node* next = pos._node->_next;

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;

			return iterator(next);
		}

8.clear()

The sentinel guard cannot be cleared, otherwise the linked list will no longer exist.

        void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				//it = erase(it);
				erase(it++);
			}
		}

9. Delete the head plug and delete the tail plug.

Directly reuse insert() and erase() here

        void push_back(const T& x)
		{
			insert(end(), x);
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}

10. Short call

bool empty()
{
    return end()==begin();
}

5. Complete code

        #pragma once
#include<assert.h>
#include<iostream>

namespace bit
{
	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _data;

		list_node(const T& x = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _data(x)
		{}
	};


	// 1、迭代器要么就是原生指针
	// 2、迭代器要么就是自定义类型对原生指针的封装,模拟指针的行为
	template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> node;
		typedef __list_iterator<T, Ref, Ptr> self;
		node* _node;

		__list_iterator(node* n)
			:_node(n)
		{}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}

		self& operator++()
		{
			_node = _node->_next;

			return *this;
		}

		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;

			return *this;
		}

		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};

	

	template<class T>
	class list
	{
		typedef list_node<T> node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;

		//typedef __list_const_iterator<T> const_iterator;

		iterator begin()
		{
			//iterator it(_head->_next);
			//return it;
			return iterator(_head->_next);
		}

		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

		iterator end()
		{
			return iterator(_head);
		}

		const_iterator end() const
		{
			//iterator it(_head->_next);
			//return it;
			return const_iterator(_head);
		}

		void empty_init()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		list()
		{
			empty_init();
		}

		template <class Iterator>
		list(Iterator first, Iterator last)
		{
			empty_init();

			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		// lt2(lt1)
		/*list(const list<T>& lt)
		{
		empty_init();
		for (auto e : lt)
		{
		push_back(e);
		}
		}*/
		void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}

		list(const list<T>& lt)
		{
			empty_init();

			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}

		// lt1 = lt3
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				//it = erase(it);
				erase(it++);
			}
		}

		void push_back(const T& x)
		{
			/*node* tail = _head->_prev;
			node* new_node = new node(x);

			tail->_next = new_node;
			new_node->_prev = tail;
			new_node->_next = _head;
			_head->_prev = new_node;*/

			insert(end(), x);
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin());
		}

		void insert(iterator pos, const T& x)
		{
			node* cur = pos._node;
			node* prev = cur->_prev;

			node* new_node = new node(x);

			prev->_next = new_node;
			new_node->_prev = prev;
			new_node->_next = cur;
			cur->_prev = new_node;
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());

			node* prev = pos._node->_prev;
			node* next = pos._node->_next;

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;

			return iterator(next);
		}
        bool empty()
        {
            return end()==begin();
        }
	private:
		node* _head;
	};
}

Guess you like

Origin blog.csdn.net/weixin_65592314/article/details/129600015