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);
	}

}
相关推荐
数据小爬虫@1 分钟前
如何利用java爬虫获得淘宝商品评论
java·开发语言·爬虫
喜欢猪猪3 分钟前
面试题---深入源码理解MQ长轮询优化机制
java
qq_172805599 分钟前
RUST学习教程-安装教程
开发语言·学习·rust·安装
天才在此12 分钟前
汽车加油行驶问题-动态规划算法(已在洛谷AC)
算法·动态规划
wjs202417 分钟前
MongoDB 更新集合名
开发语言
monkey_meng20 分钟前
【遵守孤儿规则的External trait pattern】
开发语言·后端·rust
一只小小汤圆24 分钟前
opencascade源码学习之BRepOffsetAPI包 -BRepOffsetAPI_DraftAngle
c++·学习·opencascade
草莓base33 分钟前
【手写一个spring】spring源码的简单实现--bean对象的创建
java·spring·rpc
legend_jz1 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
嘿BRE1 小时前
【C++】几个基本容器的模拟实现(string,vector,list,stack,queue,priority_queue)
c++