[STL] список использования и пробная версия _ реализация нижнего уровня

Оглавление

Один, список использует

1. Введение в список документов 

2. Общие интерфейсы

1. сортировать в списке

2. Сравнение эффективности между списком + сортировкой и вектором + сортировкой

3. Об инвалидации итератора

4. очистить

Во-вторых, реализация списка

1. Каркасная конструкция 

2. Класс Iterator — основная структура

3. оператор-> реализация 

4. const - итератор

5. вставить

6. стереть

7. ясно — осознать

8. Копируйте конструкцию 

Сначала реализуйте конструктор итератора:

 Повторное использование конструкции копирования:

9. оператор=

10. Полный код 

эпилог


 

Один, список использует

1. Введение в список документов 

1. Список — это последовательный контейнер, который можно вставлять и удалять в любом месте в пределах постоянного диапазона, а контейнер можно повторять вперед и назад.
2. Нижний слой списка представляет собой структуру двусвязного списка.Каждый элемент в двусвязном списке хранится в независимом узле, не связанном друг с другом.В узле указатели указывают на предыдущий элемент и следующий элемент .
3. list очень похож на forward_list : основное отличие состоит в том, что forward_list — это односвязный список , который можно повторять только вперед, что делает его проще и эффективнее.
4. По сравнению с другими последовательными контейнерами (array , vector , deque) list обычно имеет лучшую эффективность выполнения для вставки и удаления элементов в любой позиции .
5. По сравнению с другими последовательными контейнерами самый большой недостаток list и forward_list в том, что он не поддерживает произвольный доступ в любой позиции , например: для доступа к шестому элементу списка он должен быть из известной позиции.
( например, head или tail ) для итерации до этой позиции, для итерации в этой позиции требуется список служебных данных линейного времени и некоторое дополнительное пространство для сохранения связанной информации каждого узла (для больших списков с меньшими элементами типа хранения Это может быть важным фактором )

URL-адрес документа STL: list — Справочник по C++ (cplusplus.com) 

 

2. Общие интерфейсы

      

STL аналогичен шаблону дизайна, и поясняются только специальные части. 

 

1. сортировать в списке

 

 Мы знаем, что в библиотеке алгоритмов есть функция сортировки, так почему же список надо писать отдельной сортировкой? ? Причина: Использование сортировки в библиотеке алгоритмов имеет обязательное условие — адреса данных непрерывны. (Базовая реализация сортировки списка в основном представляет собой нерекурсивную сортировку слиянием .)

2. Сравнение эффективности между списком + сортировкой и вектором + сортировкой

    Сразу вывод : интерфейс сортировки списка не имеет большого значения .

Эксперимент эффективности:

Тестовая группа: используйте список, чтобы принять данные, и используйте сортировку списка для сортировки.

Контрольная группа: сначала используйте вектор для получения данных, затем используйте сортировку в библиотеке алгоритмов для сортировки и, наконец, передайте данные в список.

Результаты эффективности времени:

Между ними нет большой разницы, когда объем данных небольшой, но когда объем данных велик, разрыв будет от 5 до 10 раз. 

3. Об инвалидации итератора

Как было сказано ранее, здесь можно временно понимать итератор как аналог указателя.Недействительность итератора означает, что узел, на который указывает итератор, недействителен, то есть узел удаляется . Поскольку базовая структура списка представляет собой двунаправленный круговой связанный список с ведущим узлом , итератор списка не станет недействительным при его вставке в список. Он станет недействительным только при удалении, и только итерация, указывающая на удаленный узел, будет признана недействительной. , другие итераторы не затрагиваются .
void TestListIterator1()
{
   int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
   list<int> l(array, array+sizeof(array)/sizeof(array[0]));
   auto it = l.begin();
   while (it != l.end())
   {
   // erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给
    其赋值
   l.erase(it); 
   ++it;
   }
}
// 改正
void TestListIterator()
{
  int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
  list<int> l(array, array+sizeof(array)/sizeof(array[0]));
  auto it = l.begin();
  while (it != l.end())
  {
  l.erase(it++); // it = l.erase(it);
  }
}

 

4. очистить

    Очистите данные связанного списка и сохраните головной узел .

 


Во-вторых, реализация списка

1. Каркасная конструкция 

  1 
  2 #include <iostream>
  3 #include <list>
  4 uising namespace std;
  5 namespace my_list
  6 {
  7   template<class T>
  8   struct list_node
  9   {
 10     list_node ( const T& val = T() ) // 针对不同的数据类型,所以用数据类型的仿函数
 11      :_val(val)
 12      ,_prev(nullptr)
 13      ,_next(nullptr)
 14     {}
 15 
 16     T _val;
 17     list_node<T>* _prev ;
 18     list_node<T>* _next ;
 19   };

      template<class T>
 22   class list                                                                                                        
 23   {
 24     typedef list_node<T> Node;
 25   public:
 26     list() // 
 27     {
 28       _head = new Node;
 29       _head->_prev = _head;
 30       _head->_next = _head;
 31     }
   private:
 45     Node* _head;
 46   };
 47 }

2. Класс Iterator — основная структура

     Раньше строки и векторы были непрерывны в физической памяти, поэтому итераторы почти подобны указателям, а данные могут быть представлены однократным разыменованием. Список не хранится непрерывно в физической памяти. Нижний уровень списка представляет собой двусвязный список с заголовком, и данные, на которые указывает головной узел, обрабатываются и обрабатываются.

struct _list_iterator  // 由于List的迭代器,表层是通过头结点进行操作,数据在头里面,一层解引用
 25     {                      // 解决不了问题
 26       typedef list_node<T> Node;
 27       typedef _list_iterator iterator;
 28       Node* _node; // 迭代器类,内部只要一个结点的指针即可
 29 
 30       _list_iterator(Node* x)
 31         : _node(x)
 32       {}
 33 
 34       // 重载迭代器*,因为结点的解引用,只是得到结点。目的:支持读,写
 35       T& operator*()
 36       {                                                                                                             
 37         return _node->_val;  
 38       }  
 39       // 重载迭代器++,list不是连续的空间,地址++不合理;目的:++后为下一个迭代器位置,且支持读,写
 40       iterator&  operator++() // 前置++  
 41       {  
 42          _node = _node->_next;
 43          return *this;
 44       }
 45       
 46 
 47       bool operator!=(const iterator& v) const 
 48       {
 49          return _node != v._node;
 50       }

Вы заметили? Мы не написали уничтожение и копирование конструкции в нашей структуре итератора ? На самом деле внутри спрятана черная курица.

      Первый — уничтожение: 1.   Указатель _node принадлежит связному списку, мы не можем случайно освободить место . 2. Для пользовательских типов система автоматически вызывает деструктор пользовательского типа.

      Второй - копирование: нам нужен только адрес цели и копируем значение. Глубокая копия не нужна, поэтому писать не нужно. 

3. оператор-> реализация 

    Предполагая, что T является пользовательским типом, вам нужно прочитать функции-члены и даже переменные-члены внутри. Тогда общий способ записи: (*it).a1, так как это указатель, будет удобнее писать стрелку 

//  实现
        T& operator*()
		{
			return _node->_data;
		}

		T* operator->()
		{
			return &(operator*()); //这个挺怪异的,马上讲解
		}

// 测试
void test2()
{
	struct pos
	{
		int a1 = 1;
		int a2 = 2;
	};
	my_list::list<pos> x;
	my_list::list<pos>::iterator it = x.begin();
	while (it != x.end())
	{
		cout << (*it).a1;
		cout << it->a1;
	}
}

 

4. const - итератор

         Обычные итераторы могут быть разыменованы для получения онтологии данных, в то время как константные итераторы могут быть разыменованы для получения онтологии, и онтология не может быть изменена. Идея реализации следующая:

Код итератора:

#include <iostream>
#include <string>
using namespace std;

namespace my_list
{
	template <class T>
	struct list_node // 不用修改
	{
		list_node(const T& data = T())
			: _data(data)
			, _next(nullptr)
			, _prv(nullptr)
		{}

		T _data;
		list_node* _next;
		list_node* _prv;
	};

	template <class T, class Ref, class Ptr>
	struct list_iterator  // 迭代器,仅修改会返回能写的函数,*, ->
	{
		typedef list_node<T> Node;
		typedef list_iterator< T, Ref, Ptr> iterator;

		Node* _node;

		list_iterator(Node* node)
			: _node(node)
		{}

		bool operator!= (const iterator& it)
		{
			return _node != it._node;
		}

		bool operator==(const iterator& it)
		{
			return _node == it._node;
		}

		iterator& operator++()
		{
			_node = _node->_next;
			return *this;
		}

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

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

		Ptr operator->()
		{
			return &(operator*());
		}

	};


	template <class T>  // 提供const迭代器类型+ const迭代器的begin(),end()
	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;

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

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

		const_iterator end() const
		{
			return const_iterator(_head);
		}


		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prv = _head;
		}

		void push_back( const T& val)
		{
			Node* tmp = new Node(val);
			tmp->_data = val;
			tmp->_next = _head;
			tmp->_prv = _head->_prv;
			_head->_prv->_next = tmp;
			_head->_prv = tmp;
		}

	private:
		Node* _head;
	};
}

 

5. вставить

    

 Реализуем самый простой 1:

        // 在当前位置插入一个数据,当前数据向后移
		iterator insert(iterator pos, const T& data)
		{
			Node* cur = pos._node;
			Node* prv = cur->_prv;
			Node* newnode = new Node(data);

			newnode->_next = cur;
			newnode->_prv = prv;
			prv->_next = newnode;
			cur->_prv = newnode;

			return iterator(newnode);
		}

 Таким образом, push_back также может повторно использовать вставку:

        void push_back( const T& val)
		{
			/*Node* tmp = new Node(val);
			tmp->_data = val;
			tmp->_next = _head;
			tmp->_prv = _head->_prv;
			_head->_prv->_next = tmp;
			_head->_prv = tmp;*/

			insert(iterator(_head), val);  // 头插同理
		}
        void push_front(const T& val)
		{
			insert(iterator(_head->_next), val);
		}

 

6. стереть

 

Удаляет позицию итератора pos, затем возвращает следующий итератор.

        iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* prv = cur->_prv;
			Node* next = cur->_next;

			prv->_next = next;
			next->_prv = prv;
			delete cur;
			return iterator(next);
		}

  Здесь завершается только основной код итератора, а другие мелкие функции предназначены только для совместного использования кода.

 

7. ясно — осознать

    При завершении очистки можно повторно использовать деструктор списка .

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

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

8. Копируйте конструкцию 

Традиционный способ написания: Скопируйте данные один за другим самостоятельно.

Современный способ написания: используйте конструктор итератора, а затем замените узел _head. 

Сначала реализуйте конструктор итератора :

        template <class input_iterator>
		list( input_iterator begin,  input_iterator end)
		{
			list_initial(); // 对头结点进行初始化
			while (begin != end)
			{
				push_back(*begin);
				++begin;
			}
		}

 Повторное использование конструкции копирования:

       void swap(list<T>& x)  // 顺便实现一个swap
		{
			std::swap(x._head, _head);
		}

		list(const list<T>& x)
		{
			list_initial();
			list tmp(x.begin(), x.end());
			swap(tmp);
		}
         // tmp 调用析构时,会将this的_headfree掉

9. оператор=

        list<T>& operator=(list<T> tmp) // 拷贝构造
		{
			swap(tmp);
			return *this;
		}

10. Полный код 

 

#include <iostream>
#include <string>
using namespace std;

namespace my_list
{
	template <class T>
	struct list_node
	{
		list_node(const T& data = T())
			: _data(data)
			, _next(nullptr)
			, _prv(nullptr)
		{}

		T _data;
		list_node* _next;
		list_node* _prv;
	};

	template <class T, class Ref, class Ptr>
	struct list_iterator
	{
		typedef list_node<T> Node;
		typedef list_iterator< T, Ref, Ptr> iterator;

		Node* _node;

		list_iterator(Node* node)
			: _node(node)
		{}

		bool operator!= (const iterator& it)
		{
			return _node != it._node;
		}

		bool operator==(const iterator& it)
		{
			return _node == it._node;
		}

		iterator& operator++()
		{
			_node = _node->_next;
			return *this;
		}

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

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

		Ptr operator->()
		{
			return &(operator*());
		}

	};


	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;

		void list_initial()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prv = _head;
		}

		list()
		{
			list_initial();
		}

		template <class input_iterator>
		list( input_iterator begin,  input_iterator end)
		{
			list_initial(); // 对头结点进行初始化
			while (begin != end)
			{
				push_back(*begin);
				++begin;
			}
		}

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

		list(const list<T>& x)
		{
			list_initial();
			list tmp(x.begin(), x.end());
			swap(tmp);
		}

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

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



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

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

		const_iterator end() const
		{
			return const_iterator(_head);
		}


		void push_back( const T& val)
		{
			/*Node* tmp = new Node(val);
			tmp->_data = val;
			tmp->_next = _head;
			tmp->_prv = _head->_prv;
			_head->_prv->_next = tmp;
			_head->_prv = tmp;*/

			insert(iterator(_head), val);  // 头插同理
		}

		void push_front(const T& val)
		{
			insert(iterator(_head->_next), val);
		}

		// 在当前位置插入一个数据,当前数据向后移
		iterator insert(iterator pos, const T& data)
		{
			Node* cur = pos._node;
			Node* prv = cur->_prv;
			Node* newnode = new Node(data);

			newnode->_next = cur;
			newnode->_prv = prv;
			prv->_next = newnode;
			cur->_prv = newnode;

			return iterator(newnode);
		}

		iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* prv = cur->_prv;
			Node* next = cur->_next;

			prv->_next = next;
			next->_prv = prv;
			delete cur;
			return iterator(next);
		}
		
		iterator Pop_back()
		{
			erase(iterator(_head->_prv));
			return _head;
		}

		iterator Pop_front()
		{
			Node* next = _head->_next->_next;
			erase(iterator(_head->_next));
			return next;
		}

	     list<T>& operator=(list<T> tmp)
		{
			swap(tmp);
			return *this;
		}
	private:
		Node* _head;
	};
}

 

эпилог

   Этот раздел здесь, спасибо, друзья, за просмотр, если у вас есть какие-либо предложения, добро пожаловать в комментарии в области комментариев, если вы приносите пользу своим друзьям, оставьте свои лайки, ваши лайки и проблемы станут блоггерами. Движущей силой творчества мастера .

Supongo que te gusta

Origin blog.csdn.net/qq_72112924/article/details/131864299
Recomendado
Clasificación