C++:模擬實現list容器(支持迭代器)

模擬實現list容器(支持迭代器)

要模擬實現一個list容器,主要就是相關頭插頭刪尾插尾刪的接口,這些非常常用。

另外有一個點灰常重要!!那就是list的迭代器,list的迭代器不能用原生指針去模擬實現,因爲鏈表的迭代器加一是下一個節點的指針,在內存中兩個節點存放的位置並不連續。

迭代器的模擬實現

因爲list的迭代器會指向下一個節點,所以我們創建一個迭代器類,這個類成員函數就是一個一個節點的指針_node。該構造函數會通過一個傳遞過來的節點來構造對象。

Node* _node;//節點的指針

//用傳過來的節點的指針構造一個ListIterator的對象
ListIterator(Node* node)
	:_node(node)
{}

鏈表的begin和end接口都是在List類裏實現的,因爲只有鏈表裏纔有節點的指針。
需要注意幾點:

  1. begin是第一個位置的迭代器,有節點的指針就能構造迭代器。
  2. begin和end都是鏈表實現的,迭代器是我傳過去一個節點,他構造一個ListIterator的對象。
  3. 迭代器不是節點的指針,它是一個ListIterator的自定義類型。
  4. ListIterator可以用節點的指針構造一個對象。
iterator begin()
{
	return iterator(_head->_next);//調它的構造函數
}
iterator end()
{
	return iterator(_head);//左閉右開區間
}
const迭代器

我們都知道迭代器不僅有普通迭代器還有const迭代器,那麼const迭代器又該怎麼寫呢?很多人可能會想在實現一個類,專門是const迭代器的,那這樣的話就會有大量的代碼冗餘了。所以怎麼辦呢?我們採用模板

博主畫了一張調用邏輯~|ू・ω・` )
他們會根據模板參數的不同自動推演Ref和Ptr的類型。這樣的話const的就會去調用const的迭代器了。實現了代碼的複用,簡潔明瞭,是不是炒雞棒(๑•̀ㅂ•́)و✧
在這裏插入圖片描述

具體代碼如下:

simulate_list.h

#pragma once

#include <iostream>
using namespace std;

//節點
template <class T>
struct ListNode
{
	T _data;
	ListNode<T>* _prev;
	ListNode<T>* _next;

	ListNode(const T& data = T())//這裏給的是缺省,T()調的是T的默認構造函數
		:_prev(nullptr)
		, _next(nullptr)
		, _data(data)
	{}
};

//迭代器
template <class T,class Ref,class Ptr>
struct ListIterator
{
	typedef ListNode<T> Node;
	typedef ListIterator<T, Ref, Ptr> Self;

	Node* _node;//節點的指針

	//用傳過來的節點的指針構造一個ListIterator的對象
	ListIterator(Node* node)
		:_node(node)
	{}

	//實現運算符的重載,比如++,節點的指針++之後就指向連續空間的下一個了,就找不到下一個節點的指針了
	//因此需要運算符的重載
	//如節點的指針++就讓他指向下一個節點,並且返回這個迭代器

	Ref operator*()//返回的是節點裏數據的引用:爲了保證可讀可寫,返回引用的話我也可以改變這個節點的值
	{
		return _node->_data;//返回的是節點的數據
	}

	Ptr operator->()//返回data的指針,data的指針裏纔會有數據
	{
		//return &_node->_data;
		return &(operator*());//取地址
	}

	//++it; -->it.operator++()
	Self& operator++()//前置++返回++之後的
	{
		_node = _node->_next;//指向下一個位置
		return *this;//返回的是下一個位置的迭代器
	}

	//it++;
	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;
	}

	//~ListIterator()
	//不需要寫,因爲節點的生命週期是跟着鏈表走的,鏈表銷燬了節點纔會銷燬
	//迭代器不是管理一個節點,它是封裝這個節點之後讓我們用同樣的方式去遍歷這個鏈表,而不暴露裏面的東西
	//我們可以理解爲迭代器就像是一個隔離層,屏蔽了裏面實現的東西,但是外面的使用都是一樣的
};

//鏈表
template <class T>
class List
{
	typedef ListNode<T> Node;//節點
public:
	typedef ListIterator<T, T&, T*> iterator;//迭代器
	typedef ListIterator<T, const T&, const T*> const_iterator;//const迭代器

	List()//構造函數
	{
		_head = new Node();
		_head->_next = _head;
		_head->_prev = _head;
	}

	//begin是第一個位置的迭代器,有節點的指針就能構造迭代器,只有鏈表纔有節點的指針
	//begin和end都是鏈表實現的,迭代器是我傳過去一個節點,他構造一個ListIterator的對象
	iterator begin()
	{
		//迭代器不是節點的指針,它是一個ListIterator的自定義類型
		//ListIterator可以用節點的指針構造一個對象
		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(const List<T>& l)//拷貝構造(拷貝構造要支持深拷貝,否則析構的時候會有問題)
	{
		this->_head = new Node;
		_head->_next = _head;
		_head->_prev = _head;

		List<int>::const_iterator it = l.begin();
		while (it != l.end())
		{
			this->PushBack(*it);
			it++;
		}
	}

	//現代寫法
	List<int>& operator=(List<int> l)
	{
		//假設l1 = l2
		//l就是l2拷貝構造出來的,因此交換this和l兩個鏈表的頭節點即可
		//交換過後l1原來的值會在l裏,l出了作用域會自動釋放(臨時對象)
		swap(this->_head, l._head);
		return *this;
	}

	~List()
	{
		Clear();
		delete _head;
		_head = nullptr;
	}

	void Clear()//鏈表的清理
	{
		Node* cur = _head->_next;//從第一個節點開始刪除
		while(cur != _head)
		{
			Node* next = cur->_next;//刪除這個節點之前要保存下一個節點
			delete cur;

			cur = next;
		}

		//不刪除頭節點,最後要把頭節點鏈接好
		_head->_next = _head;
		_head->_prev = _head;
	}
	void PushBack(const T& x)
	{
		//方法一
		//Node* tail = _head->_prev;
		//Node* newnode = new Node(x);
		//tail->_next = newnode;
		//newnode->_prev = tail;
		//_head->_prev = newnode;
		//newnode->_next = _head;

		//方法二
		Insert(end(), x);//在end()的前面插入
	}
	void PopBack()
	{
		Erase(--end());//end()指向的是最後一個的下一個,因此尾刪的是--end()
	}
	void PushFront(const T& x)
	{
		Insert(begin(), x);//在begin()的前面插入
	}
	void PopFront()
	{
		Erase(begin());//begin()是頭節點的下一個,頭刪就是刪除這個節點
	}
	void Insert(iterator pos, const T& x)
	{
		Node* cur = pos._node;
		Node* prev = cur->_prev;
		Node* newnode = new Node(x);
		prev->_next = newnode;
		newnode->_prev = prev;
		newnode->_next = cur;
		cur->_prev = newnode;
	}
	iterator Erase(iterator pos)
	{
		Node* cur = pos._node;
		Node* prev = cur->_prev;
		Node* next = cur->_next;
		prev->_next = next;
		next->_prev = prev;
		delete cur;

		return iterator(next);//返回下一個位置的迭代器
	}

	size_t Size()
	{
		size_t size = 0;
		for (const auto& e : *this)
		{
			size++;
		}
		return size;
	}

	bool Empty()
	{
		//return _head->_next = _head;
		return begin() == end();
	}
private:
	Node* _head;
};

test.cpp

這部分主要就是對我們模擬實現的List的測試遼!

#include "simulate_list.h"

void test_list1()
{
	List<int> l;
	l.PushBack(1);
	l.PushBack(2);
	l.PushBack(3);
	l.PushBack(4);

	//普通迭代器
	List<int>::iterator it = l.begin();
	while (it != l.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	for (auto& e : l)
	{
		cout << e << " ";
	}
	cout << endl;
}

void PrintList(const List<int>& list)
{
	//const迭代器
	List<int>::const_iterator it = list.begin();
	while (it != list.end())
	{
		//*it = 10;//不可以修改
		cout << *it << " ";
		it++;
	}
	cout << endl;
}
void test_list2()
{
	List<int> l;
	l.PushBack(1);
	l.PushBack(2);
	l.PushBack(3);
	l.PushBack(4);

	PrintList(l);//調用const迭代器
}
void test_list3()
{
	List<int> l;
	l.PushBack(1);
	l.PushBack(2);
	l.PushBack(3);
	l.PushBack(4);

	l.PushFront(0);
	l.PushFront(-1);
	l.PushFront(-2);
	l.PushFront(-3);

	l.PopFront();
	l.PopBack();

	PrintList(l);
}
void test_list4()
{
	List<int> l;
	l.PushBack(1);
	l.PushBack(2);
	l.PushBack(3);
	l.PushBack(4);

	List<int> ll(l);//用l拷貝構造ll

	PrintList(ll);

	List<int> lt;
	lt.PushBack(10);
	lt.PushBack(20);
	lt.PushBack(30);
	lt.PushBack(40);

	ll = lt;//用lt給ll賦值

	PrintList(ll);

	cout << ll.Size() << endl;

}
void test_list5()
{
	List<int> l;
	l.PushBack(1);
	l.PushBack(2);
	l.PushBack(3);
	l.PushBack(4);
	l.PushBack(5);
	//刪除所有偶數
	List<int>::iterator it = l.begin();
	while (it != l.end())
	{
		if (*it % 2 == 0)
		{
			//Erase返回的是下一個位置的迭代器,再重新賦值給it,讓它繼續往後走
			it = l.Erase(it);
		}
		else
		{
			it++;
		}
	}
	PrintList(l);
}
int main()
{
	//test_list1();
	//test_list2();
	//test_list3();
	//test_list4();
	test_list5();
	system("pause");
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章