C++ list的模拟实现

一 定义节点类

list相当于带头节点的双向链表,我们定义节点时要用类模板参数,同时定义_next、_prev指针和数据_data,使用struct定义节点类,因为节点类要能够被访问,而struct的默认访问权限就是public(当然手动更改权限为public也可),构造函数缺省值要使用匿名对象,保证无论是自定义类型还是内置类型都能够构造成功。

cpp 复制代码
template<class T>
struct list_node
{
	T _data;
	list_node<T>* _next;
	list_node<T>* _prev;

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

二 list的基本框架

list是带头节点的双向链表,所以成员变量我们只需要定义一个头结点_head,但是链表的长度我们无法像vector一样通过指针的加减法进行求取,因此我们加入一个size变量进行对链表长度的求取。

默认构造函数则就是让头节点指向自己,但是list不只有一个构造函数,所以我们讲头节点指向自己这一部分单独封装为一个函数,从而让更多构造函数使用。

代码实现如下:

cpp 复制代码
	template<class T>
	class list {
		typedef list_node<T> Node;
	public:
		list() {
			empty_init();
		}

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

			_size = 0;
		}
	private:
		Node* _head;
        size_t _size;
	};

三 list的迭代器

vector和string的底层物理空间是连续的,我们可以通过指针的++或--来移动找到对应的元素,然而list的底层物理空间是不连续的,所以模拟实现list迭代器时,如果使用++或 --操作,++或 --的只是一个指针,并不能找到对应的位置。因此我们可以封装一个list迭代器类,实际上就是对结点指针进行封装,将各种运算符进行重载,使得在list类中能够像vector和string一样能够直接使用迭代器,而不用关心底层实现。

但注意,我们的迭代器分为const迭代器和非const迭代器,莫非我们需要写两个迭代器吗?我们都知道两个迭代器的区别:

const_iterator不允许您更改它们指向的值,常规iterator可以。

显然,两个迭代器只有在外部进行修改的时候有所不同,在内部实现并没有什么差别,因此我们可以复用一个模板,但需要三个模板参数,在list内部我们传入T ,T* ,T& 或者是 T ,const T*, const T&,这样就可以保持两个迭代器的区别,并且可以复用同一段代码。

3.1 普通迭代器的实现

代码在list内部如下定义:

cpp 复制代码
	template<class T>
	class list {
		typedef list_node<T> Node;
	public: 
       //区分两个迭代器
		typedef __list_iterator<T, T&, T*> iterator;

		list() {
			empty_init();
		}

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

			_size = 0;
		}
	private:
		Node* _head;
	};

迭代器代码:

cpp 复制代码
	// 三个模板参数
	// T T& T*
	// T cosnt T& const T*
	template<class T, class Ref, class Ptr>
	struct __list_iterator{

		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> self;
		Node* _node;
       
        //用一个节点创造iterator
		__list_iterator(Node* node)
			:_node(node)
		{}

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

		self& operator--(){
			_node = _node->_prev;
			return *this;
		}

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

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

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

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

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

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

当我们实现上述代码之后我们就可以在list类中实现begin和end函数:

cpp 复制代码
	iterator begin()
	{
		//也可以写成这样的形式
		// return _head->next 通过返回值自动构造
		return iterator(_head->_next);
	}

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

3.2 const迭代器的实现

cpp 复制代码
	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;

		//将begin和end函数补全
		const_iterator begin() const
		{
            //返回的是const迭代器类型,避免在外部修改数据
			return const_iterator(_head->_next);
		}

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

四 list的常用接口模拟实现

链表中只要实现了insert和erase,头插头删和尾插尾删都可以实现,所以我们先实现insert和erase函数。

4.1 insert函数

在pos位置之前插入一个新结点。

借用双链表的思想实现即可。

ps:传入的值为返回值方便复用其它函数。

cpp 复制代码
//在pos位置之前插入一个新结点
iterator insert(iterator pos, const T& val){
    
	Node* cur = pos._node;
	Node* newnode = new Node(val);

	Node* prev = cur->_prev;
	
	prev->_next = newnode;
	newnode->_prev = prev;

	newnode->_next = cur;
	cur->_prev = newnode;

	++_size;
	//返回当前节点位置
	return iterator(newnode);
}

4.2 push_back()

尾插

直接复用insert即可

cpp 复制代码
	void push_back(const T& x)
		{
			insert(end(), x);
		}

4.3 push_front()

头插

直接复用insert

cpp 复制代码
		//头插
		void push_front(const T& x){
			insert(begin(), x);
		}		
	

4.4 erase() 函数

删除pos位置的结点。

cpp 复制代码
iterator erase(iterator pos) {
	//头节点不能删除
	assert(pos != _head);

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

	delete cur;
	prev->_next = next;
	next->_prev = prev;
	
	--_size;

	return iterator(next);
}

注意迭代器失效问题,这里需要返回值。

4.5 pop_back()

尾删

cpp 复制代码
	void pop_back(){
		//注意这里定义的end节点位置
		erase(--end());
	}

4.6 pop_front()

头删

cpp 复制代码
	void pop_front()
	{
		erase(begin());
	}

五 容量接口

5.1 size函数

返回节点个数。

直接返回size大小即可。

cpp 复制代码
size_t size() const {
	return _size;
}

5.2 clear函数

清空list

复用erase即可

cpp 复制代码
	void clear(){
		iterator it = begin();
		while (it != end()){
			it = erase(it);
		}
	}

六 函数补全

6.1 构造函数

cpp 复制代码
//拷贝构造
list(const list<T>& lt) {
	empty_init();
	for (auto e : it) {
		push_back(e);
	}
}

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

}


//移动构造
list(list<T>&& lt) {
	swap(lt);
}

//迭代器区间构造函数
template<class Iterator>
list(Iterator first, Iterator last)
{
	empty_init();
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

6.2 赋值重载

cpp 复制代码
	//赋值重载 
	list<T>& operator()(const list<T>& lt) {

		list<T> tmp(lt);
		swap(tmp);
		return *this;
	}

	//移动赋值
	list<T>& operator()(list<T>&& lt) {
		swap(it);
		return *this;
	}

6.3 析构函数

复用clear函数,记得delete head节点,并且置空。

cpp 复制代码
		~list(){
			clear();
			delete _head;
			_head = nullptr;
		}

七 完整代码+ 测试代码

cpp 复制代码
#pragma once
#include<iostream>
#include<cassert>
#include<vector>
#include<set>
using namespace std;

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

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

	// 三个模板参数
	// T T& T*
	// T cosnt T& const T*
	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* node)
			:_node(node)
		{}

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

		self& operator--(){
			_node = _node->_prev;
			return *this;
		}

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

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

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

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

		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;

		//将begin和end函数补全
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

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

		iterator begin()
		{
			//也可以写成这样的形式
			// return _head->next 通过返回值自动构造
			return iterator(_head->_next);
		}

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


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


        //拷贝构造
		list(const list<T>& lt) {
			empty_init();
			for (auto e : lt) {
				push_back(e);
			}
		}

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

		}


		//移动构造
		list(list<T>&& lt) {
			swap(lt);
		}

		//迭代器区间构造函数
		template<class Iterator>
		list(Iterator first, Iterator last)
		{
			empty_init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//赋值重载 
		list<T>& operator=(const list<T>& lt) {

			list<T> tmp(lt);
			swap(tmp);
			return *this;
		}

		//移动赋值
		list<T>& operator=(list<T>&& lt) {
			swap(lt);
			return *this;
		}

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

			_size = 0;
		}

		//在pos位置之前插入一个新结点
		iterator insert(iterator pos, const T& val){
		    
			Node* cur = pos._node;
			Node* newnode = new Node(val);

			Node* prev = cur->_prev;
			
			prev->_next = newnode;
			newnode->_prev = prev;

			newnode->_next = cur;
			cur->_prev = newnode;

			++_size;
			//返回当前节点位置
			return iterator(newnode);
		}

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

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

		iterator erase(iterator pos) {
			//头节点不能删除
			assert(pos != _head);

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

			delete cur;
			prev->_next = next;
			next->_prev = prev;
			
			--_size;

			return iterator(next);
		}

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

		void pop_back(){
			//注意这里定义的end节点位置
			erase(--end());
		}

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

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


		size_t size() const {
			return _size;
		}
	private:
		Node* _head;
		size_t _size;
	};

	void test_list1()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		// 封装屏蔽底层差异和实现细节
		// 提供统一的访问修改遍历方式
		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			*it += 20;

			cout << *it << " ";
			++it;
		}
		cout << endl;

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

	}

	void test_list2()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);

		list<int> lt1(lt);

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

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

		list<int> lt2;
		lt2.push_back(10);
		lt2.push_back(200);
		lt2.push_back(30);
		lt2.push_back(40);
		lt2.push_back(50);

		lt1 = lt2;
		for (auto e : lt1)
		{
			cout << e << " ";
		}
		cout << endl;

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

		for (auto e : lt1)
		{
			e++;
			cout << e << " ";
		}
		cout << endl;


		for (auto e : lt2)
		{
			cout << e << " ";
		}

		cout << endl;
	}

	struct AA
	{
		AA(int a1 = 0, int a2 = 0)
			:_a1(a1)
			, _a2(a2)
		{}

		int _a1;
		int _a2;
	};

	void test_list3()
	{
		list<AA> lt;
		lt.push_back(AA(1, 1));
		lt.push_back(AA(2, 2));
		lt.push_back(AA(3, 3));

		list<AA>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << it->_a1 << " " << it->_a2 << endl;
			cout << it.operator->()->_a1 << " " << it.operator->()->_a1 << endl;

			++it;
		}
		cout << endl;

	}


	template<typename Container>
	void print_container(const Container& con)
	{
		typename Container::const_iterator it = con.begin();
		while (it != con.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

	void test_list4()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);
		//print_list(lt);
		print_container(lt);

		list<string> lt1;
		lt1.push_back("1111111111111");
		lt1.push_back("1111111111111");
		lt1.push_back("1111111111111");
		lt1.push_back("1111111111111");
		lt1.push_back("1111111111111");
		//print_list(lt1);
		print_container(lt1);

		vector<string> v;
		v.push_back("222222222222222222222");
		v.push_back("222222222222222222222");
		v.push_back("222222222222222222222");
		v.push_back("222222222222222222222");
		//print_list(v);
		print_container(v);
	}

}
相关推荐
AndrewHZ5 小时前
【复杂网络分析】如何入门Louvain算法?
python·算法·复杂网络·社区发现·community det·louvain算法·图挖掘
froginwe115 小时前
SQLite Indexed By
开发语言
AndrewHZ5 小时前
【图像处理基石】如何基于黑白图片恢复出色彩?
图像处理·深度学习·算法·计算机视觉·cv·色彩恢复·deoldify
虾说羊5 小时前
JVM 高频面试题全解析
java·开发语言·jvm
雨中飘荡的记忆5 小时前
MyBatis SQL解析模块详解
java·mybatis
czlczl200209255 小时前
Spring Cache 全景指南
java·后端·spring
POLITE35 小时前
Leetcode 3.无重复字符的最长子串 JavaScript (Day 4)
javascript·算法·leetcode
invicinble5 小时前
透视IDEA,IDEA认识到什么程度算精通
java·ide·intellij-idea
wanzhong23335 小时前
NLS开发日记1-初始化项目
java·项目
毕设源码-赖学姐5 小时前
【开题答辩全过程】以 基于PHP的国学诗词网站与推荐系统的设计与实现为例,包含答辩的问题和答案
开发语言·php