透过源码看本质:list 的模拟实现与核心原理

一、STL源码

定义节点

迭代器

list定义


成员函数


初始化

从内存池中获取空间

创建节点

定位new 初始化

销毁节点


二、模拟实现 list 容器

1. list成员变量

cpp 复制代码
//定义节点
template<class T>
struct list_node
{
	list_node<T>* _prev;//指向前驱节点
	list_node<T>* _next;//指向后继结点
	T val;//存储值

	//默认构造函数
	list_node(const T& x = T())
		:_prev(nullptr)
		, _next(nullptr)
		, val(x)
	{
	}
};
//容器list
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;
private:
	Node* _head;
	size_t _size;
	//list_node _head;
};

2. 构造函数

cpp 复制代码
//初始化
void empty_init()
{
	//带头节点
	_head = new Node;
	_head->_prev = _head;
	_head->_next = _head;

	_size = 0;
}
//默认构造函数
list()
{
	empty_init();
}

list是一个带头的双向循环链表,因此,构造时创建一个带头的节点

3. insert

cpp 复制代码
void insert(const iterator& pos, const T& x)
//void insert(iterator pos, const T& x)
{
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	
	Node* newnode = new Node(x);
	newnode->_prev = prev;
	prev->_next = newnode;
	newnode->_next = cur;
	cur->_prev = newnode;
	
	++_size;
}

list容器数据结构之带头双向循环链表这篇文章的原理是一样的,只不过进行了封装而已 。看不懂细节的,可以移步这篇文章。list容器,重点讲解迭代器部分

4. push_back

cpp 复制代码
void push_back(const T& x)
{
		Node* newnode = new Node(x);
		Node* tail = _head->_prev;
		tail->_next = newnode;
		newnode->_prev = tail;
		newnode->_next = _head;
		_head->_prev = newnode;
	
		++_size;
}

其实,push_back就是尾插,可以复用 insert 函数。这样去实现。

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

这样是不是简单多了,end返回的就是list容器中的末尾后的一个位置(即头结点),而插入节点是在该位置之前插入的

5. 拷贝构造

cpp 复制代码
//lt2(lt1)
list(list<T>& lt)
{
	empty_init();
	for (const auto& e : lt)
	{
		push_back(e);
	}
}

拷贝构造也需要先初始化 list 对象,在调用 push_back 逐个插入元素

6. 初始化列表构造

cpp 复制代码
list(initializer_list<T> lt)
{
	empty_init();
	for (const auto& e : lt)
	{
		push_back(e);
	}
}

逐个访问初始化列表中的元素,进行插入

7. operator=

cpp 复制代码
void swap(list<T>& lt)
{
	std::swap(_head, lt._head);
	std::swap(_size, lt._size);
}

//lt3=lt2
//拷贝构造+交换
list<T>& operator=(list<T> lt)
{
	swap(lt);
	return *this;
}

通过拷贝构造出一个局部对象,在进行资源的交换就完成赋值重载了

8. erase

cpp 复制代码
iterator erase(iterator pos)
{
	assert(pos != end());
	Node* cur = pos._node;

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

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

	//隐式类型转换
	return iterator(next);
}

请看数据结构之带头双向循环链表这篇文章。

9. pop_back,push_front,pop_front

cpp 复制代码
void pop_back()
{
	erase(--end());
}
void push_front(const T& x)
{
	insert(begin(), x);
}
void pop_front()
{
	erase(begin());
}

直接复用insert,erase函数即可

10. clear

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

清空 list 中的所有元素,从 list 中的 begin 位置的节点开始删除

11. 析构函数

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

清空 list 容器中的所有节点,再删除头结点,更新_size

12. size

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

13. 迭代器

cpp 复制代码
iterator begin()
{
	//_head->_next是一个指向list_node节点的指针
	//iterator是一个list_iterator的类
	//隐式类型转换,借助构造函数
	//返回的是iterator的临时对象
	//return _head->_next;

	return iterator(_head->_next);
}
iterator end()
{
	//隐式类型转换
	//return _head;

	return iterator(_head);
}

const_iterator begin()const
{
	return const_iterator(_head->_next);
}
const_iterator end()const
{
	//隐式类型转换
	//return _head;

	return const_iterator(_head);
}

以前使用库里面的迭代器,都是直接获取迭代器,该迭代器指向某个元素的这块空间,通过解引用就可以访问到该元素 。那么,在 list 这里,可以直接用原生指针Node*(list_node<T>*)作为迭代器吗?答案是不可以的

因为如果用原生指针Node*(内置类型)作为迭代器,那么解引用能访问到元素,但是使用迭代器的方式就会与平时不一样,需要手动访问数据,而且原生指针是一个内置类型,要如何通过++操作访问下一个节点呢 ?基于以上原因,因此,不能用原生指针作为迭代器

那要如何处理呢?

我们可以对原生指针进行封装,使之成为一个自定义类型 ,这样不就可以通过解引用操作,调用运算符重载函数了,访问到数据了吗!其次,自定义类型在++操作的时候,也可以通过运算符重载指向下一个Node节点

具体实现如下。

cpp 复制代码
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)
	{
	}

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

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

	Self& operator++()
	{
		_node = _node->_next;
		return *this;
	}
	Self operator++(int)
	{
		//浅拷贝,默认拷贝构造函数就可以用
		list_iterator<T> tmp(*this);
		_node = _node->_next;
		//局部变量,不能用引用返回
		return tmp;
	}

	Self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}
	Self operator--(int)
	{
		list_iterator<T> tmp(*this);
		_node = _node->_prev;
		return tmp;
	}
	bool operator!=(const Self& it)
	{
		return _node != it._node;
	}
	bool operator==(const Self& it)
	{
		return _node == it._node;
	}
};

可以看到,这里不仅有模板参数T,还多了两个模板参数Ref 和 Ptr。实现迭代器的时候,不仅实现了普通迭代器,还实现了 const 版本的迭代器,如果不增加这两个模板参数,我们就还需要写一个类来封装Node*指针 ,而且,这两个类的代码基本是一样的,在返回值,参数类型上会有所区别,代码会非常的冗余 。所以,添加两个模板参数就可以很好的解决这个问题了

三、完整代码实现

. list.h

cpp 复制代码
#pragma once


#include<iostream>
#include<assert.h>
using namespace std;

namespace LC
{
	template<class T>
	struct list_node
	{
		list_node<T>* _prev;
		list_node<T>* _next;
		T val;

		//默认构造函数
		list_node(const T& x = T())
			:_prev(nullptr)
			, _next(nullptr)
			, val(x)
		{
		}
	};

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

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

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

		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		Self operator++(int)
		{
			//浅拷贝,默认拷贝构造函数就可以用
			list_iterator<T> tmp(*this);
			_node = _node->_next;
			//局部变量,不能用引用返回
			return tmp;
		}

		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		Self operator--(int)
		{
			list_iterator<T> tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		bool operator!=(const Self& it)
		{
			return _node != it._node;
		}
		bool operator==(const Self& it)
		{
			return _node == it._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;

		iterator begin()
		{
			//_head->_next是一个指向list_node节点的指针
			//iterator是一个list_iterator的类
			//隐式类型转换,借助构造函数
			//返回的是iterator的临时对象
			//return _head->_next;

			return iterator(_head->_next);
		}
		iterator end()
		{
			//隐式类型转换
			//return _head;

			return iterator(_head);
		}

		const_iterator begin()const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end()const
		{
			//隐式类型转换
			//return _head;

			return const_iterator(_head);
		}

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

			_size = 0;
		}

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

		//lt2(lt1)
		list(list<T>& lt)
		{
			empty_init();
			for (const auto& e : lt)
			{
				push_back(e);
			}
		}

		list(initializer_list<T> lt)
		{
			empty_init();
			for (const auto& e : lt)
			{
				push_back(e);
			}
		}

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

		//lt3=lt2
		//拷贝构造+交换
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

		size_t size()const
		{
			return _size;
		}
		void push_back(const T& x)
		{
				Node* newnode = new Node(x);
				Node* tail = _head->_prev;
				tail->_next = newnode;
				newnode->_prev = tail;
				newnode->_next = _head;
				_head->_prev = newnode;

				++_size;
			//insert(end(), x);
		}
		//在pos位置之前插入
		void insert(const iterator& pos, const T& x)
			//void insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;

			Node* newnode = new Node(x);
			newnode->_prev = prev;
			prev->_next = newnode;
			newnode->_next = cur;
			cur->_prev = newnode;

			++_size;
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());
			Node* cur = pos._node;

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

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

			//隐式类型转换
			return iterator(next);
		}

		void pop_back()
		{
			erase(--end());
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		void pop_front()
		{
			erase(begin());
		}

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

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
			_size = 0;
		}
	private:
		Node* _head;
		size_t _size;
		//list_node _head;
	};
}

. test.cc

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"list.h"
struct A
{
	int _a1;
	int _a2;
	A(int a1 = 1, int a2 = 2)
		:_a1(a1)
		, _a2(a2)
	{
	}
};
void testlist1()
{
	LC::list<int> l1;

	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(3);
	l1.push_back(4);


	LC::list<int>::iterator it = l1.begin();
	//l1.end()返回的是临时变量,形参必须是const&
	while (it != l1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	l1.insert(l1.begin(), 5);
	l1.insert(l1.end(), 6);

	it = l1.begin();
	while (it != l1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	l1.erase(l1.begin());
	it = l1.begin();
	while (it != l1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

void testlist2()
{
	LC::list<int> l1;

	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(3);
	l1.push_back(4);

	LC::list<int> l2(l1);

	LC::list<int>::iterator it = l2.begin();
	while (it != l2.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	cout << l2.size() << endl;

	LC::list<int> l3;
	l3.push_back(10);
	l3.push_back(20);
	l3.push_back(30);
	l3.push_back(40);
	l3.push_back(50);

	l2 = l3;
	LC::list<int>::iterator it1 = l2.begin();
	while (it1 != l2.end())
	{
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;
	cout << l2.size() << endl;

	//LC::list<double> l4 = { 100,200,300,400 };
	LC::list<double> l4 = { 1.1,2.2,3.3,4.4 };
	LC::list<double>::iterator it2 = l4.begin();
	while (it2 != l4.end())
	{
		cout << *it2 << " ";
		++it2;
	}
	cout << endl;
	cout << l4.size() << endl;

	LC::list<A> l5;
	l5.push_back({ 1,1 });
	l5.push_back({ 2,2 });
	l5.push_back({ 3,3 });
	l5.push_back({ 4,4 });
	LC::list<A>::iterator it3 = l5.begin();
	while (it3 != l5.end())
	{
		cout << it3->_a1 << " " << it3->_a2 << endl;
		++it3;
	}
	cout << endl;
	cout << l5.size() << endl;

}
int main()
{
	testlist2();

	return 0;
}

今天的文章分享到此结束,感谢观看。

相关推荐
李日灐2 小时前
改造红黑树实现封装 map/set:感受C++ 标准容器的精妙设计与底层实现
开发语言·数据结构·c++·后端·算法·红黑树
李日灐2 小时前
【优选算法1】双指针经典算法题
数据结构·c++·后端·算法·刷题·双指针
故事和你912 小时前
sdut-程序设计基础Ⅰ-期末测试(重现)
大数据·开发语言·数据结构·c++·算法·蓝桥杯·图论
Ralph_Y2 小时前
C++ 常量的定义方式与内存存储位置全解析
c++
重庆兔巴哥2 小时前
如何在Dev-C++中使用MinGW-w64编译器?
linux·开发语言·c++
ysa0510302 小时前
贪心【逆向dp】
数据结构·c++·笔记·算法
魔道不误砍柴功2 小时前
Java Function 高级使用技巧:从工程实战中来
java·开发语言·python
三佛科技-187366133972 小时前
LP3783A芯茂微5V2.1A低功耗原边反馈充电器芯片替代PL3378/C
c语言·开发语言
不知名。。。。。。。。2 小时前
仿muduo库实现高并发服务器----EventLoop与线程整合起来
java·开发语言·jvm