C++语法——超详细模拟实现list源代码

这篇小编将以SGI版的list为例进行模拟实现。

目录

一.主体框架

二.具体实现

(一).节点node

(二).主体list(封装,函数)

①对节点封装

②构造函数、拷贝构造函数

③析构函数

④头插头删push/pop_front、尾插尾删push/pop_back

⑤定向插入insert

⑥定向删除erase

⑦赋值

⑧长度size与扩容resize

⑨swap

⑩其他函数empty/front/back/begin/end/rbegin/rend

(三).迭代器iterator 

(四).反向迭代器reverse_iterator

三.完整代码


 

一.主体框架

这里小编做了一张图,它足以表明list的大致框架:

C++语法——超详细模拟实现list源代码_第1张图片

 

先有框架再来一点点具体去了解~ 

二.具体实现

 

(一).节点node

C++语法——超详细模拟实现list源代码_第2张图片

对于节点node而言,我们可以按照c语言中的双向链表节点结构进行创建。

首先node设为一种类,由于list属于泛型编程,我们不知道链表数据类型,因此使用template将node变成泛型类。内部参数分别是node* next,node* prev,T value。

大致结构如下:

template
class list_node
{
public:
	list_node(T _value = T())//构造函数,使用匿名对象作为缺省
		:value(_value)
		,next(nullptr)
		,prev(nullptr)
	{}
	T value;//节点数据
	list_node* next;//下一个节点
	list_node* prev;//上一个节点
};

 

 通过代码我们可以发现,在结构上list_node与c语言的结构体链表节点基本无异。

但是C++特性赋予了链表节点泛型编程的强大功能。

 

(二).主体list(封装,函数)

C++语法——超详细模拟实现list源代码_第3张图片

当我们使用list模板时,不可能自己一个个创建节点,再将之连接,否则就与c语言无异了。

因此,需要对节点进一步封装并加上与之配套的函数。由此我们需要创建list类。

 

①对节点封装

其实这很简单,我们只需要在list类内部定义一个node*成员变量作为哨兵位头节点,即node* head。换句话说,其实list内部成员就是链表的头节点。

当我们需要操作链表时,从头节点开始即可。

 

②构造函数、拷贝构造函数

默认情况下,我们只需要一个头节点即可,即next、prev都指向自己。

如图所示:

C++语法——超详细模拟实现list源代码_第4张图片

typedef list_node Node;
void list_init()//成员函数中不免要使用list初始化,不妨专门定义一个这样的函数
{
	head = new Node;
	head->next = head;
	head->prev = head;
}
List() 
{
	list_init();
}

 当然list构造函数也应该支持按数量和类型构造

list(int n, const T& value = T())
{
	list_init();
	for (int i = 0; i < n; ++i)
		push_back(value);//后文会有实现
}
 

除此之外我们发现,官方list还支持按照迭代器的方式构造

a954a98d47d247dd91a3f0aa728bc089.png

在这里我们可以专门定义一个模板用于表示迭代器类型,这样不管何种迭代器都可以进行构造:

template
List(InputIterator start, InputIterator end)
{
	list_init();
	while (start != end)
	{
		push_back(*start);
		start++;
	}
}

 需要注意,list的拷贝构造函数应该是深拷贝

List(const Node& list)
{
	Node tmp(list.begin(), list.end());//使用上述构造函数
	swap(tmp);//专门定义一个函数用于交换两个list头节点即可
}

 

③析构函数

对于析构函数而言,最重要的是清空链表节点,以防内存泄漏。

~list()
{
	Node* cur = head->next;
	while (cur != head)
	{
		head->next = cur->next;
		delete cur;
		cur = head->next;
	}

	head->next = head->prev = head;//将链表闭环
	delete head;
	head = nullptr;
}

 

④头插头删push/pop_front、尾插尾删push/pop_back

这里的实现思路与普通的带头双向链表无异,具体可以参考这篇博客:带头双向循环链表基础知识归纳

 

⑤定向插入insert

首先看一下官方定义:

7001e55fbf384f67a12c4472d4c5b213.png

9dbf655e558e499695976cf80513fbfb.pngec3a91ddac6f4ec18a48f63d73624888.png

 我们可知,insert是在选择的迭代器前插入数据,且返回值是插入的第一个元素的迭代器。

那么实现方式就出来了,在pos位置之前插入一个元素即可:

iterator insert(iterator pos, const T& val)
{
	Node* pNewNode = new Node(val);
	Node* pCur = pos.node;
	pNewNode->prev = pCur->prev;
	pNewNode->next = pCur;
	pNewNode->prev->next = pNewNode;
	pCur->prev = pNewNode;
	return iterator(pNewNode);
}

当然,批量化插入也可以实现,只需要按所需插入数量while循环调用insert即可,注意返回值是第一次插入的数据的迭代器。

对于参数为迭代器的insert函数只需要从头到尾遍历一遍参数迭代器,将之插入pos之前即可。

 

⑥定向删除erase

C++语法——超详细模拟实现list源代码_第5张图片

4dea289cfb20412580403ce44b4451c2.png 从官方定义中我们知道,erase会删除position所代表的迭代器或从first迭代器开始直到last(last不删),返回值是被删除元素的下一个位置的迭代器。

 实现方式很简单,我们只需要把pos之前与之后的节点相连即可:

iterator erase(iterator pos)
{
	Node* pDel = pos.node;
	Node* pRet = pDel->next;
	pDel->prev->next = pDel->next;
	pDel->next->prev = pDel->prev;
	delete pDel;
	return iterator(pRet);
}

对于参数为迭代器的同理,只需要把first之前节点与last节点相连,清空原先first与last之间节点,返回last节点。

 

⑦赋值

对于赋值而言,本质是深拷贝,因此,直接在传参时调用拷贝构造深拷贝即可。

Node& operator=(Node l)//参数是实参的拷贝构造
{
	swap(l);//调用list成员函数swap
	return *this;
}

 

⑧长度size与扩容resize

size的实现与c中一样,遍历链表即可。

size_t size()const
{
	Node* cur = head->next;
	size_t count = 0;
	while (cur != head)
	{
		count++;
		cur = cur->next;
	}

	return count;
}

扩容需要判断是增容还是缩容。增容push_back,缩容pop_back。

void resize(size_t newsize, const T& data = T())
{
	size_t oldsize = size();//成员函数size
	if (newsize <= oldsize)//缩容
	{
		while (newsize < oldsize)
		{
			pop_back();
			oldsize--;
		}
	}
	else//扩容
	{
		while (oldsize < newsize)
		{
			push_back(data);
			oldsize++;
		}
	}
}

 

⑨swap

对于swap函数而言直接交换两个list的头节点指针即可。

void swap(Node& list)
{
	std::swap(head, list.head);
}

 

⑩其他函数empty/front/back/begin/end/rbegin/rend

这一部分比较简单,直接看下文完整代码即可。

 

(三).迭代器iterator 

C++语法——超详细模拟实现list源代码_第6张图片

对于正向迭代器而言,其使用了类模板而不是单纯使用一个指针实现。

具体原因可以参考这篇博客: 为什么STL中List的实现其迭代器iterator是类模板?

当我们在实现迭代器的时候,需要时刻记得它在表面上看是一个链表节点的指针。因此,其内部一定有一个指向链表节点的指针,还需要支持所有与指针有关的操作:

template
class list_iterator {
	typedef list_node Node;
	typedef list_iterator Self;
    typedef list_iterator iterator;
	typedef list_iterator const_iterator;

public://用于reverse_iterator
	typedef Ref Ref;
	typedef Ptr Ptr;
	typedef T T;

public:
	list_iterator(Node* _node)
		:node(_node)
	{}
    //支持const_iterator接收iterator
    list_iterator(const iterator& it)
		: node(it.node)
	{}

	Ref operator*() {
		return node->value;
	}
	Ptr operator->() {
		return &(node->value);
	}
	
	bool operator==(const Self& it) const{
		return node == it.node;
	}
	bool operator!=(const Self& it) const{
		return node != it.node;
	}
	Self operator++() {
		node = node->next;
		return *this;
	}
	Self operator++(int) {
		iterator tmp(node);
		node = node->next;
		return tmp;
	}
	Self operator--() {
		node = node->prev;
		return *this;
	}
	Self operator--(int) {
		iterator tmp(node);
		node = node->prev;
		return tmp;
	}


	Node* node;
};

当然,小编在这里利用拷贝构造函数实现了由普通iterator隐式类型转化成const_iterator。

具体原理在此:STL中list如何实现普通迭代器隐式类型转换成const迭代器

 

(四).反向迭代器reverse_iterator

C++语法——超详细模拟实现list源代码_第7张图片

SGI版的list对反向迭代器的实现非常巧妙,所谓反向迭代器就是正向的倒序,那么我们可以在内部封装一个正向迭代器,反向++就是正向--,反向--就是正向++。

template
class _reverse_iterator
{
    //typename用于明确这是类型名,否则编译器会将之与类成员混淆,
    //因为类成员也使用::作用域符访问
	typedef typename Iterator::Ref Ref;
	typedef typename Iterator::Ptr Ptr;
	typedef typename Iterator::T T;
	typedef _reverse_iterator Self;
	typedef list_iterator iterator;
	typedef _reverse_iterator reverse_iterator;
public:
	_reverse_iterator(const iterator& it)
		:_it(it)
	{}
	//支持const_reverse_iterator接收reverse_iterator
	_reverse_iterator(const reverse_iterator& rit)
		: _it(rit._it)
	{}

	Self operator++(){
		_it--;
		return *this;
	}
	Self operator--(){
		_it++;
		return *this;
	}
	Ref operator*() {
		iterator tmp = _it;
		tmp--;
		return *tmp;
	}
	Ref operator->(){
		iterator tmp = _it;
		tmp--;
		return &(*tmp);
	}
    bool operator==(const Self& rit)const{
		return _it != rit._it;
	}
	bool operator!=(const Self& rit)const{
		return _it != rit._it;
	}
private:
	Iterator _it;//内部封装了一个正向迭代器
};

当然,const_reverse_iterator接收普通反向迭代器也是通过拷贝构造函数实现,其参数是一个普通反向迭代器。 本质还是调用正向迭代器的拷贝构造。

 

三.完整代码

看在小编如此用心的份上,点赞支持一下吧

//链表节点
template
class list_node
{
public:
	list_node(T _value = T())
		:value(_value)
		,next(nullptr)
		,prev(nullptr)
	{}
	T value;
	list_node* next;
	list_node* prev;
};

//正向迭代器
template
class list_iterator {
	typedef list_node Node;
	typedef list_iterator Self;
    typedef list_iterator iterator;
	typedef list_iterator const_iterator;

public://用于reverse_iterator
	typedef Ref Ref;
	typedef Ptr Ptr;
	typedef T T;

public:
	list_iterator(Node* _node)
		:node(_node)
	{}
    //支持const_iterator接收iterator
    list_iterator(const iterator& it)
		: node(it.node)
	{}

	Ref operator*() {
		return node->value;
	}
	Ptr operator->() {
		return &(node->value);
	}
	
	bool operator==(const Self& it) const{
		return node == it.node;
	}
	bool operator!=(const Self& it) const{
		return node != it.node;
	}
	Self operator++() {
		node = node->next;
		return *this;
	}
	Self operator++(int) {
		iterator tmp(node);
		node = node->next;
		return tmp;
	}
	Self operator--() {
		node = node->prev;
		return *this;
	}
	Self operator--(int) {
		iterator tmp(node);
		node = node->prev;
		return tmp;
	}

	Node* node;
};

//反向迭代器
template
class _reverse_iterator
{
    //typename用于明确这是类型名,否则编译器会将之与类成员混淆,
    //因为类成员也使用::作用域符访问
	typedef typename Iterator::Ref Ref;
	typedef typename Iterator::Ptr Ptr;
	typedef typename Iterator::T T;
	typedef _reverse_iterator Self;
	typedef list_iterator iterator;
	typedef _reverse_iterator reverse_iterator;
public:
	_reverse_iterator(const iterator& it)
		:_it(it)
	{}
	//支持const_reverse_iterator接收reverse_iterator
	_reverse_iterator(const reverse_iterator& rit)
		: _it(rit._it)
	{}

	Self operator++(){
		_it--;
		return *this;
	}
	Self operator--(){
		_it++;
		return *this;
	}
	Ref operator*() {
		iterator tmp = _it;
		tmp--;
		return *tmp;
	}
	Ref operator->(){
		iterator tmp = _it;
		tmp--;
		return &(*tmp);
	}
    bool operator==(const Self& rit)const{
		return _it != rit._it;
	}
	bool operator!=(const Self& rit)const{
		return _it != rit._it;
	}
private:
	Iterator _it;//内部封装了一个正向迭代器
};

//主体
template
class List {
	typedef list_node Node;

public:
	typedef list_iterator iterator;
	typedef list_iterator const_iterator;
	typedef _reverse_iterator reverse_iterator;
	typedef _reverse_iterator const_reverse_iterator;

	void list_init()
	{
		head = new list_node;
		head->next = head;
		head->prev = head;
	}
	List() 
	{
		list_init();
	}
    template
	List(InputIterator start, InputIterator end)
	{
		list_init();
		while (start != end)
		{
			push_back(*start);
			start++;
		}
	}
	List(const Node& list)
	{
		Node tmp(list.begin(), list.end());
		swap(tmp);
	}
    List(int n, const T& value = T())
    {
	    list_init();
	    for (int i = 0; i < n; ++i)
		    push_back(value);
    }
    Node& operator=(Node l)//参数是实参的拷贝构造
	{
		swap(l);//调用list成员函数swap
		return *this;
	}

    ~List()
    {
	    Node* cur = head->next;
	    while (cur != head)
	    {
		    head->next = cur->next;
		    delete cur;
		    cur = head->next;
	    }

	    head->next = head->prev = head;//将链表闭环
	    delete head;
	    head = nullptr;
    }

	iterator begin() {
		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);
	}

	reverse_iterator rbegin() {
		return reverse_iterator(end());
	}
	reverse_iterator rend() {
		return reverse_iterator(begin());
	}
	const_reverse_iterator rbegin() const{
		return const_reverse_iterator(end());
	}
	const_reverse_iterator rend() const{
		return const_reverse_iterator(begin());
	}


    size_t size()const
    {
	    Node* cur = head->next;
	    size_t count = 0;
	    while (cur != head)
	    {
		    count++;
		    cur = cur->next;
	    }

	    return count;
    }
    void resize(size_t newsize, const T& data = T())
    {
	    size_t oldsize = size();//成员函数size
	    if (newsize <= oldsize)//缩容
	    {
		    while (newsize < oldsize)
		    {
			    pop_back();
			    oldsize--;
		    }
	    }
	    else//扩容
	    {
		    while (oldsize < newsize)
		    {
			    push_back(data);
			    oldsize++;
		    }
	    }
    }
	void push_back(const T& x)
	{
		Node* newNode = new Node(x);
		Node* tail = head->prev;
		newNode->prev = tail;
		tail->next = newNode;
		newNode->next = head;
		head->prev = newNode;
	}
	void push_front(const T& x)
	{
		Node* newNode = new Node(x);
		Node* first = head->next;
		newNode->next = first;
		first->prev = newNode;
		newNode->prev = head;
		head->next = newNode;
	}
	void pop_back() {
		assert(head->next != head);
		Node* tail = head->prev;
		Node* new_tail = tail->prev;
		new_tail->next = head;
		head->prev = new_tail;
		delete tail;
	}
	void pop_front() {
		assert(head->next != head);
		Node* front = head->next;
		Node* new_front = front->next;
		head->next = new_front;
		new_front->prev = head;
		delete front;
	}
    iterator insert(iterator pos, const T& val)
    {
	    Node* pNewNode = new Node(val);
		Node* pCur = pos.node;
		pNewNode->prev = pCur->prev;
		pNewNode->next = pCur;
		pNewNode->prev->next = pNewNode;
		pCur->prev = pNewNode;
		return iterator(pNewNode);
    }
    iterator erase(iterator pos)
    {
	    Node* pDel = pos.node;
		Node* pRet = pDel->next;
		pDel->prev->next = pDel->next;
		pDel->next->prev = pDel->prev;
		delete pDel;
		return iterator(pRet);
    }

	void swap(Node& list)
	{
		std::swap(head, list.head);
	}
    
    bool empty()const
	{
		return head->next == head;
	}
	T& front()
	{
		return head->next->value;
	}

	const T& front()const
	{
		return head->next->value;
	}
	T& back()
	{
		return head->prev->value;
	}
	const T& back()const
	{
		return head->prev->value;
	}
private:
	Node* head;
};

 

 

 

大多数优秀的程序员从事编程工作,不是因为期望获得报酬或得到公众的称赞,而是因为编程是件有趣的事儿——Linus Torvalds


如有错误,敬请斧正

 

你可能感兴趣的:(C++语法,list正向,反向迭代器实现,list的增删查改,模拟实现list及函数接口,C++/STL/list源代码,带头双向循环链表数据结构)