ライブラリを使用してリスト ホイールを作成する [C++]

list のモック実装

デフォルトのメンバー関数

コンストラクタ

list は、先頭の双方向循環リンク リストです。リスト オブジェクトを構築するときは、新しい先頭ノードが必要で、その prev と next はそれ自体を指します。

  	void empty_init()
		{
    
    
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;

		}
	//默认构造
		list()
		{
    
    
			empty_init();
		}

コピーコンストラクター

//拷贝构造函数
list(const list<T>& lt)
{
    
    
	_head = new node; //申请一个头结点
	_head->_next = _head; //头结点的后继指针指向自己
	_head->_prev = _head; //头结点的前驱指针指向自己
	for (auto & e : lt) //两个 e都是同一个
	{
    
    
		push_back(e); //将容器lt当中的数据一个个尾插到新构造的容器后面
	}
}

代入演算子のオーバーロード

バージョン 1 (推奨):
パラメーターは参照を使用しません。コンパイラーは自動的にリストのコピー コンストラクターを呼び出してリスト オブジェクトを構築し、次に swap 関数を呼び出して元のコンテナーとリスト オブジェクトを交換します。

これは、clear でクリーンアップすべきデータを Exchange 関数を通じてコン​​テナ lt に渡すのと同じであり、代入演算子のオーバーロード関数呼び出しが終了すると、コンテナ lt は自動的に破棄され、そのデストラクタが clean のために呼び出されます。上。


		list<T> & operator= (list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			//list<T>& operator= (  list<T> * this, list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			// lt1 = lt2
		{
    
    
			this->swap(lt);
			return *this; 
		  }

バージョン 2:
まず、clear 関数を呼び出して元のコンテナをクリアし、次にコンテナ lt 内のデータをトラバーサルによって空のコンテナに 1 つずつ挿入します。

  
list<T>& operator=(const list<T>& lt)
{
    
    
	if (this != &lt) //避免自己给自己赋值
	{
    
    
		clear(); //清空容器
		for (const auto& e : lt)
		{
    
    
			push_back(e); //将容器lt当中的数据一个个尾插到链表后面
		}
	}
	return *this; //支持连续赋值
}

デストラクター

オブジェクトを破棄するときは、最初にclear関数を呼び出してコンテナ内のデータをクリアし、次にヘッドノードを解放し、最後にヘッドポインタを空に設定します。

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

			}
			_size = 0;
		}

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

イテレータ

イテレータはなぜ存在するのでしょうか?

文字列とベクトルのイテレータ

string と Vector はデータを連続メモリ空間に格納します。その後、ポインタを介して自動インクリメント、自動デクリメント、逆参照などの操作を実行でき、対応する位置にあるデータに対して一連の操作を実行できます。ベクトルは自然な反復子です

ここに画像の説明を挿入

リストの反復子リストのメモリ内での各ノードの位置はランダムであり、必ずしも連続しているとは限らず、ノードの自己インクリメント、自己デクリメント、デリファレンスなどの操作だけでは、対応するノードのデータを操作できません。ポインタ.

ここに画像の説明を挿入

const_iterator

const イテレータでは、const イテレータが指す内容は変更できません。つまり、逆参照によって返された値は変更できません。イテレータ自体は変更可能で、解決策は 2 つあります:
1. const イテレータ クラスをカプセル化する

	template< class T>
	//const 迭代器 ,让迭代器指向的内容不能修改, 迭代器本身可以修改
	struct __list_const_iterator
	{
    
    
		typedef list_node<T>  Node;

		//构造函数
		__list_const_iterator(Node* node)
			:_node(node)
		{
    
    

		}

		const T& operator*()//出了作用域,节点的值还在,用引用
			//const: 返回节点的值,不能修改
		{
    
    
			return _node->_val;
		}

		//前置++,返回++之后的值
		__list_const_iterator& operator++()
			//__list_const_iterator& operator++(__list_const_iterator  * this  )
		{
    
    
			_node = _node->_next;
			return *this;
		}
		//后置++ ,返回++之前的值
		__list_const_iterator operator++(int)
		{
    
    
			__list_const_iterator tmp(*this);
			_node = _node->_next;
			return tmp;// tmp出了作用域就被销毁 ,用传值返回 
		}

		bool operator==(const __list_iterator<T>& it)
		{
    
    
			return *this == it._node;
		}
		bool operator!=(const __list_iterator<T>& it)//传值返回,返回的是拷贝,是一个临时对象,临时对象具有常性
		{
    
    
			return *this != it._node;
		}
		Node* _node;
	};

2テンプレートパラメータを追加してコードを再利用することを選択します (推奨)

template<class T, class Ref, class Ptr>

ここに画像の説明を挿入

C++ ライブラリが解決策です

	//template<class T> //list类存储的数据是任意类型,所以需要设置模板参数
	//普通迭代器
	//Ref是引用 ,Ptr是指针
	template<class T,class Ref,class Ptr>
	struct  __list_iterator
	{
    
    
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr>  self;

		//构造函数
		__list_iterator(Node* node)
			:_node(node)
		{
    
    

		}

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

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

		//前置++,返回++之后的值
		self & operator++()
			//__list_iterator<T> & operator++(__list_iterator<T> * this  )
		{
    
    
			_node = _node->_next;
			return *this;

		}
		//后置++ ,返回++之前的值
	      self  operator++(int)
			//	__list_iterator<T> operator++( __list_iterator<T> * this ,int)
		{
    
    
			self tmp(*this);//拷贝构造
			_node = _node->_next;
			return tmp; // tmp出了作用域就被销毁 ,用传值返回 
		}
		bool operator!= (const self& it)
		{
    
    
			return _node != it._node;
		}
		bool operator== (const self & it)
		{
    
    
			return _node == it._node;
		}
		Node* _node;
	};
	template<class T>//list类存储的数据是任意类型,所以需要设置模板参数
	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 end()  //最后一个数据的下一个位置,即头节点
		{
    
    
			//return _head; // _head的类型是list_node<T>* ,iterator的类型是__list_iterator<T> ,类型不一致,涉及到单参数的构造函数支持隐式类型转换 
			//还可以写成 return iterator(_head);
			return iterator(_head);
		}
		iterator begin()//第一个数据的位置,即头节点的下一个位置
		{
    
    
			//return _head->_next;//单参数的构造函数支持隐式类型转换
			//还可以写成 return iterator(_head->_next)
			return iterator(_head->_next);
		}

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

		const_iterator end() const
		{
    
    
			return const_iterator(_head);
		}
		//默认构造
		list()
		{
    
    
			empty_init();
		}
		// lt2(lt1)
		//还没有实现const_iterator
		list(const list<T>& lt)
		{
    
    
			empty_init();
			//拷贝数据
			for (auto & e :lt )//遍历lt
			{
    
    
				push_back(e);
			}
		}
		~list()
		{
    
    
			clear();
			delete _head;
			_head = nullptr;
		}
		void empty_init()
		{
    
    
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;

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

		list<T> & operator= (list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			//list<T>& operator= (  list<T> * this, list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			// lt1 = lt2
		{
    
    
			this->swap(lt);
			return *this; 
		  }


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

			}
			_size = 0;
		}
		void push_back(const T& x)
		{
    
    
			insert(end(), x);//在最后一个数据的下一个位置插入
		}
		//pos位置之前插入
		iterator insert(iterator pos, const T& x)
		{
    
    
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);
			// prev newnode cur 链接关系
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			++_size;
			return newnode;
		}
		iterator erase (iterator pos)

		{
    
    
			assert(pos != end());
			Node* cur = pos._node;
			Node* next = cur->_next;
			Node* prev = cur->_prev;
			//prev next 
			prev->_next = next;
			next->_prev = prev;

			delete cur;
			--_size;
			return next;
		}

		size_t size()
		{
    
    
			return _size;
		}
		void push_front( const T & x )//T可能是vector ,用引用,减少拷贝
		{
    
    
			insert(begin(),x);
		}
		void pop_back()
		{
    
    
			erase(--end());//end是最后一个数据的下一个位置,需要--,到达最后一个数据,这样才是尾删
		}
		void pop_front()
		{
    
    
			erase(begin());
		}
	private:
		Node* _head;
		size_t _size;
	};

const オブジェクトを定義すると、const 変更されたイテレータが自動的に呼び出されます。const 装飾されたイテレータを呼び出すと、__list_iterator のテンプレート パラメータが const T& としてインスタンス化されます。実際、インスタンス化するとき、const と非 const は依然として 2 つの異なるクラスですが、インスタンス化のコード作業はコンパイラに渡されます。

始まりと終わり

リストの場合、最初の有効なデータのイテレータはヘッド ノードの後のノードです。begin
関数は最初の有効なデータのイテレータ、つまりヘッド ノードの次の位置を返します。end
関数は最後の有効なデータを返します。 . 次の位置、つまりヘッドノードへの反復子

		iterator end()  //最后一个数据的下一个位置,即头节点
		{
    
    
			return _head; // _head的类型是list_node<T>* ,iterator的类型是__list_iterator<T> ,类型不一致,涉及到单参数的构造函数支持隐式类型转换 
			//还可以写成 return iterator(_head);
		}
		iterator begin()//第一个数据的位置,即头节点的下一个位置
		{
    
    
			return _head->_next;//单参数的构造函数支持隐式类型转换
			//还可以写成 return iterator(_head->_next)
		}

constオブジェクトのbegin関数とend関数

	const_iterator begin() const
		{
    
    
			return const_iterator(_head->_next);//返回使用头结点后一个结点
		}

		const_iterator end() const
		{
    
    
			return const_iterator(_head);//返回使用头结点
		}

入れる

ここに画像の説明を挿入

prev newnode cur 間のリンク関係を再変更します

 	//pos位置之前插入
		iterator insert(iterator pos, const T& x)
		{
    
    
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);
			// prev newnode cur 链接关系
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			++_size;
			return newnode;
		}

消す

ここに画像の説明を挿入
prev と next のリンク関係を変更し、cur を解放します。

iterator erase (iterator pos)
		{
    
    
			assert(pos != end());
			Node* cur = pos._node;
			Node* next = cur->_next;
			Node* prev = cur->_prev;
			//prev next 
			prev->_next = next;
			next->_prev = prev;
            delete cur ;
			--_size;
			return next;
		}

プッシュバック && ポップバック

	void push_back(const T& x)
		{
    
    
			insert(end(), x);//在最后一个数据的下一个位置插入
		}
	void pop_back()
		{
    
    
			erase(--end());//end是最后一个数据的下一个位置,需要--,到达最后一个数据,这样才是尾删
		}

プッシュフロント&&ポップフロント

		void pop_front()
		{
    
    
			erase(begin());
		}
		void push_front( const T & x )//T可能是vector ,用引用,减少拷贝
		{
    
    
			insert(begin(),x);
		}

スワップ

swap関数は2つのコンテナを交換するために使用されます. リストコンテナにはリンクリストの先頭ポインタとサイズが格納されています. 2つのコンテナの先頭ポインタとサイズを交換します.

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

注: ここでライブラリ内のスワップ テンプレート関数を呼び出すには、スワップ関数の前に「std::」を追加して、C++ 標準ライブラリでスワップ関数を探すようにコンパイラに指示する必要があります。そうしないと、コンパイラは、スワップ関数を使用していると認識します。実装されているスワップ関数を呼び出す (近接原理)

要約する

ここに画像の説明を挿入

完全なコード

#pragma once
#include<iostream>
#include<assert.h>
#include<list>
using namespace std;
namespace cxq
{
    
    
	//list类存储的数据是任意类型,所以需要设置模板参数
	template<class T>
	//节点
	struct list_node
	{
    
    

		//构造函数
		list_node(const T& val = T()) //缺省值是匿名对象,c++对内置类型进行了升级
			:_prev(nullptr)
			, _next(nullptr)
			, _val(val)
		{
    
    
			
		}

		list_node<T>* _prev;
		list_node<T>* _next;
		T _val;
	};

	//template<class T> //list类存储的数据是任意类型,所以需要设置模板参数
	//普通迭代器
	//Ref是引用 ,Ptr是指针
	template<class T,class Ref,class Ptr>
	struct  __list_iterator
	{
    
    
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr>  self;

		//构造函数
		__list_iterator(Node* node)
			:_node(node)
		{
    
    

		}

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

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

		//前置++,返回++之后的值
		self & operator++()
			//__list_iterator<T> & operator++(__list_iterator<T> * this  )

		{
    
    
			_node = _node->_next;
			return *this;

		}
		//后置++ ,返回++之前的值
	      self  operator++(int)
			//	__list_iterator<T> operator++( __list_iterator<T> * this ,int)

		{
    
    
			self tmp(*this);//拷贝构造
			_node = _node->_next;
			return tmp; // tmp出了作用域就被销毁 ,用传值返回 
		}
		bool operator!= (const self& it)
		{
    
    
			return _node != it._node;
		}
		bool operator== (const self & it)
		{
    
    
			return _node == it._node;
		}

		Node* _node;
	};


	//template< class T>
	const 迭代器 ,让迭代器指向的内容不能修改, 迭代器本身可以修改
	//struct __list_const_iterator
	//{
    
    
	//	typedef list_node<T>  Node;

	//	//构造函数
	//	__list_const_iterator(Node* node)
	//		:_node(node)
	//	{
    
    

	//	}

	//	const T& operator*()//出了作用域,节点的值还在,用引用
	//		//const: 返回节点的值,不能修改
	//	{
    
    
	//		return _node->_val;
	//	}

	//	//前置++,返回++之后的值
	//	__list_const_iterator& operator++()
	//		//__list_const_iterator& operator++(__list_const_iterator  * this  )
	//	{
    
    
	//		_node = _node->_next;
	//		return *this;
	//	}
	//	//后置++ ,返回++之前的值
	//	__list_const_iterator operator++(int)
	//	{
    
    
	//		__list_const_iterator tmp(*this);
	//		_node = _node->_next;
	//		return tmp;// tmp出了作用域就被销毁 ,用传值返回 
	//	}

	//	bool operator==(const __list_iterator<T>& it)
	//	{
    
    
	//		return *this == it._node;
	//	}
	//	bool operator!=(const __list_iterator<T>& it)//传值返回,返回的是拷贝,是一个临时对象,临时对象具有常性
	//	{
    
    
	//		return *this != it._node;
	//	}
	//	Node* _node;
	//};


	template<class T>//list类存储的数据是任意类型,所以需要设置模板参数
	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;//const 迭代器


		//迭代器 
		//能直接显示构造最好显式构造,不要把决定权给编译器进行单参数的隐式类型转换
		iterator end()  //最后一个数据的下一个位置,即头节点
		{
    
    
			//return _head; // _head的类型是list_node<T>* ,iterator的类型是__list_iterator<T> ,类型不一致,涉及到单参数的构造函数支持隐式类型转换 
			//还可以写成 return iterator(_head);
			return iterator(_head);
		}
		iterator begin()//第一个数据的位置,即头节点的下一个位置
		{
    
    
			//return _head->_next;//单参数的构造函数支持隐式类型转换
			//还可以写成 return iterator(_head->_next)
			return iterator(_head->_next);
		}

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

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


		//默认构造
		list()
		{
    
    
			empty_init();
		}
		// lt2(lt1)
		//还没有实现const_iterator
		list(const list<T>& lt)
		{
    
    
			empty_init();
			//拷贝数据
			for (auto & e :lt )//遍历lt
			{
    
    
				push_back(e);
			}
		}

		~list()
		{
    
    
			clear();
			delete _head;
			_head = nullptr;
		}
		void empty_init()
		{
    
    
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;

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

		list<T> & operator= (list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			//list<T>& operator= (  list<T> * this, list<T>  lt)//右值没有引用传参,间接调用拷贝构造
			// lt1 = lt2
		{
    
    
			this->swap(lt);
			return *this; 
		  }


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

			}
			_size = 0;
		}
		void push_back(const T& x)
		{
    
    
			找尾
			//Node* tail = _head->_prev;
			//Node* newnode = new Node(x);
			改变链接关系 
			///*newnode = tail->next;*/
			//tail->_next = newnode;
			//newnode->_prev = tail;

			//_head->_prev = newnode;
			//newnode->_next = _head;

			insert(end(), x);//在最后一个数据的下一个位置插入

		}
		//pos位置之前插入
		iterator insert(iterator pos, const T& x)
		{
    
    
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);
			// prev newnode cur 链接关系
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			++_size;
			return newnode;
		}
		iterator erase (iterator pos)

		{
    
    
			assert(pos != end());
			Node* cur = pos._node;
			Node* next = cur->_next;
			Node* prev = cur->_prev;
			//prev next 
			prev->_next = next;
			next->_prev = prev;

			delete cur;
			--_size;
			return next;
		}

		size_t size()
		{
    
    
			return _size;
		}
		void push_front( const T & x )//T可能是vector ,用引用,减少拷贝
		{
    
    
			insert(begin(),x);
		}
		void pop_back()
		{
    
    
			erase(--end());//end是最后一个数据的下一个位置,需要--,到达最后一个数据,这样才是尾删
		}
		void pop_front()
		{
    
    
			erase(begin());
		}

	private:
		Node* _head;
		size_t _size;
	};
	void test_list1()
	{
    
    
		list<int> lt1;
		lt1.push_back(1);
		lt1.push_back(2);

		list<int>::iterator it = lt1.begin();//拷贝构造
		while (it != lt1.end())
		{
    
    
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}
	void test_list2()
	{
    
    
		list<int> lt1;
		lt1.push_back(1);
		lt1.push_back(2);

		list<int> lt2 (lt1);
		for (auto e : lt1)
		{
    
    
			cout << e << " ";
		}
		cout << endl;
	}
}



この記事が少しでも役に立ったと思っていただけましたら、「いいね!」「集めて転送」をして、Xi Ling に注目していただけますと、皆さんの応援が私のモチベーションに変わります!

おすすめ

転載: blog.csdn.net/qq_73478334/article/details/132189416