【C++】STL—— list 模拟实现
文章目录
- 📕 list 简介
- 📕 list 模拟实现
- 框架
- ★ 迭代器实现 ★
- ★反向迭代器的实现★
- 构造函数、拷贝构造
- 插入、删除
- 其他成员函数
- 📕 源代码
- iterator.h
- list.h
📕 list 简介
vector 是一个和数组类似的容器,list 实际上就是和链表类似的容器。它对数据的操作,本质上就是对链表的增删查改,底层是双向链表。如果对双向带头循环链表不熟悉,可以尝试先看一看这篇文章:双向带头循环链表的实现。
对比链表和顺序表,vector 和 list 的优劣势也类似的。list 可以在任意位置高效地增删查改,但是不可以随机访问容器里的数据。
📕 list 模拟实现
框架
框架如下,首先,list 是链表的形式,所以必须要定义一个struct 来存储链表的节点,__list_node 就是存储节点的结构体。由于是双向链表,所以每个节点有两个指针。
其次,和 string、vector 不同的是,list 将原生指针进行封装,成了迭代器(前两个是直接把指针当作迭代器)。至于为什么迭代器的模板参数有三个,后面实现的时候会有详细解释。
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace simulate
{
template<class T>
struct __list_node
{
__list_node<T>* _prev;
__list_node<T>* _next;
T _data;
__list_node(const T& x=T())
:_prev(nullptr)
,_next(nullptr)
,_data(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 * x)
:_node(x)
{}
};
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;
};
}
★ 迭代器实现 ★
这里的迭代器本质上是对原生指针的封装,所以其成员变量是节点的指针。
里面的各个运算符重载也不过多赘述了,实际上就是对链表的操作,熟悉链表这些一看就理解了。
关键是,如何实现 const_iterator 呢?迭代器虽然是指向一个节点,但是实际上需要的是节点里存放的 _data 数据,节点里的其余两个指针无所谓,只是为了让迭代器++、-- 。所以,const_iterator 也只需要保证节点内的 _date 数据不被改变即可。至于指针,无所谓,依然可以进行插入、删除。
那么,实现 const_iterator ,对于迭代器而言,实际上只需要用 const 修饰 operator*() 重载(解引用)的返回值。但是这样就要重新定义一个 const_list_iterator 类,它和 __list_iterator 的区别仅仅是类名和 operator*() 重载的返回值。(不可以重载,因为仅仅是返回值不同。)
所以,这里要借鉴STL的方法,那就是传第二个模板参数,这个参数是 operator*() 的返回值类型。如下,在 list 类里面分别定义 iterator 和 const_iterator 。
typedef __list_iterator<T,T&> iterator;
typedef __list_iterator<T, const T&> const_iterator;
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 * x)
:_node(x)
{}
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self tmp = _node;
_node = _node->_next;
return tmp;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return (&_node->_data);
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self tmp = _node;
_node = _node->_prev;
return tmp;
}
bool operator==(const self& x)
{
return _node == x._node;
}
bool operator!=(const self& x)
{
return _node != x._node;
}
};
此外, operator->() 也值得重载一下。否则,定义一个迭代器 it ,实际上就是定义了 __list_iterator 的一个对象,那么要通过 -> 访问数据,就要先 it._node ,拿到节点指针,然后才可以 it._node->_date 。这样子怪怪的,所以重载 operator->() ,返回值应该要是 T* ,即数据类型的指针,但是考虑到要区分是否是 const 迭代器,所以又要传入一个模板参数 Ptr ,以此来作为operator->() 的返回值。
所以,最终在 list 中定义iterator 和 const_iterator 如下。第一个模板参数是数据类型,第二个模板参数是 operator*() 的返回值类型,第三个模板参数是 operator->() 的返回值类型。
typedef __list_iterator<T,T&,T*> iterator;
typedef __list_iterator<T, const T&,const T*> const_iterator;
★反向迭代器的实现★
反向迭代器利用了萃取技术。它是将 正向 迭代器进行封装,其成员变量就是一个正向迭代器,成员函数是根据需求,调用正向迭代器的成员函数。
例如,反向迭代器的 ++ ,实际上就是正向迭代器的 --。
实现了下面的反向迭代器的类,对于任意类型,都适用!!!为什么,因为这只不过是对正向迭代器的封装,反向迭代器里面的一切,都是对正向迭代器的成员函数的封装,只要类里面实现了正向迭代器,然后再加入下列代码,就可以实现反向迭代器!
template<class Iterator, class Ref, class Ptr>
struct ReverseIterator
{
typedef ReverseIterator<Iterator, Ref, Ptr> Self;
Iterator _cur;
ReverseIterator(Iterator it)
:_cur(it)
{}
Ref operator*()
{
Iterator tmp = _cur;
--tmp;
return *tmp;
}
Self& operator++()
{
--_cur;
return *this;
}
Self& operator--()
{
++_cur;
return *this;
}
bool operator!=(const Self& s)
{
return _cur != s._cur;
}
};
构造函数、拷贝构造
初始化就是建立一个头节点。并且让它的 _prev 、 _next 指针都指向自己。
构造函数主要就是一个迭代器区间的构造,两个区间之内尾插数据即可。
拷贝构造可以复用构造函数,按照迭代器区间构造一个 list 对象 tmp,然后将这个 tmp 和 *this 互换一下,实际上就是互换两个对象的成员变量(指向链表头节点的指针)。 swap 函数的参数必须要是引用。
void empty_init()
{
_head = new node;
_head->_next = _head;
_head->_prev = _head;
}
// 初始化一个list,先有一个哨兵位节点(双向带头循环链表)
list()
{
empty_init();
}
void swap(list<T>& x)
{
std::swap(_head, x._head);
}
list(const list<T>& x)
{
empty_init();
list<T> tmp(x.begin(), x.end()); // 这是构造函数
swap(tmp);
}
template<class InputIterator>
list(InputIterator start,InputIterator finish)
{
empty_init();
while (start._node != finish._node)
{
push_back(*start);
++start;
}
}
插入、删除
当实现 insert() 、erase() 之后,其他的插入、删除 都可以复用这两个函数。
insert() 、 erase() 其本质上也就是和链表类似的,并没有什么要注意的地方。
析构函数值得一看,因为 erase() 是删除单个节点,而析构是要删除整个链表,所以可以在析构里面复用 erase() ,最后也不要忘了要删除头节点。
void push_back(const T& x)
{
//node* tail = _head->_prev;
//node* tmp = new node(x);
//tail->_next = tmp;
//tmp->_prev = tail;
//tmp->_next = _head;
//_head->_prev = tmp;
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
void insert(iterator pos, const T& val) //在 pos 位置之前插入
{
node* cur = pos._node;
node* prev = cur->_prev;
node* tmp = new node(val);
prev->_next = tmp;
tmp->_prev = prev;
tmp->_next = cur;
cur->_prev = tmp;
}
iterator erase(iterator pos) // 删除当前位置的数据
{
assert(pos != end());
node* prev = pos._node->_prev;
node* next = pos._node->_next;
delete pos._node;
prev->_next = next;
next->_prev = prev;
return iterator(next);
}
~list<T>()
{
iterator it = begin();
while (it != end())
{
erase(it++);//后置++ 在这里体现作用了,返回it,但是实际上 it 已经++
}
delete _head;
_head = nullptr;
}
其他成员函数
赋值重载的思想和拷贝构造类似,都是先构造出一个 list ,然后交换两个 list 对象的成员变量(也就是指向链表头节点的指针),这样就完成了赋值。由于参数没有传引用,所以相对于形参是实参的拷贝,使用 swap 不会影响实参。
其他的 begin() 、 end() 没啥好说的,begin() 返回的是指向第一个节点的迭代器,end 返回的是最后一个节点的下一个(即头节点)。
list<T> operator=( list<T> x)
{
swap(x);
return *this;
}
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
// 这里 const 修饰的是 *this, *this 是 list 的一个对象,其成员变量是一个指针
// 指针本身固然是不可以改变的,但是其指向的内容可以改变
// 可是这样的话,如何实现 const_iterator 呢?
const_iterator begin()const
{
return const_iterator(_head->_next);
}
const_iterator end()const
{
return const_iterator(_head);
}
📕 源代码
iterator.h
#pragma once
namespace simulate_iterator
{
template<class Iterator, class Ref, class Ptr>
struct ReverseIterator
{
typedef ReverseIterator<Iterator, Ref, Ptr> Self;
Iterator _cur;
ReverseIterator(Iterator it)
:_cur(it)
{}
Ref operator*()
{
Iterator tmp = _cur;
--tmp;
return *tmp;
}
Self& operator++()
{
--_cur;
return *this;
}
Self& operator--()
{
++_cur;
return *this;
}
bool operator!=(const Self& s)
{
return _cur != s._cur;
}
};
}
list.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace simulate
{
template<class T>
struct __list_node
{
__list_node<T>* _prev;
__list_node<T>* _next;
T _data;
__list_node(const T& x=T())
:_prev(nullptr)
,_next(nullptr)
,_data(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 * x)
:_node(x)
{}
// 这里不需要写拷贝构造,直接用编译器自动生成的浅拷贝即可
self& operator++()
{
_node = _node->_next;
return *this;
}
self operator++(int)
{
self tmp = _node;
_node = _node->_next;
return tmp;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return (&_node->_data);
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator--(int)
{
self tmp = _node;
_node = _node->_prev;
return tmp;
}
bool operator==(const self& x)
{
return _node == x._node;
}
bool operator!=(const self& x)
{
return _node != x._node;
}
};
template<class T>
class list
{
typedef __list_node<T> node;
public:
typedef __list_iterator<T,T&,T*> iterator;
//typedef __list_const_iterator<T> const_iterator;
typedef __list_iterator<T, const T&,const T*> const_iterator;
typedef simulate_iterator::ReverseIterator<iterator, T&, T*> reverse_iterator;
typedef simulate_iterator::ReverseIterator<iterator, const T&, const T*> const_reverse_iterator;
void empty_init()
{
_head = new node;
_head->_next = _head;
_head->_prev = _head;
}
// 初始化一个list,先有一个哨兵位节点(双向带头循环链表)
list()
{
empty_init();
}
void swap(list<T>& x)
{
std::swap(_head, x._head);
}
list(const list<T>& x)
{
empty_init();
list<T> tmp(x.begin(), x.end());
swap(tmp);
}
template<class InputIterator>
list(InputIterator start,InputIterator finish)
{
empty_init();
while (start._node != finish._node)
{
push_back(*start);
++start;
}
}
list<T> operator=( list<T> x)
{
swap(x);
return *this;
}
void push_back(const T& x)
{
//node* tail = _head->_prev;
//node* tmp = new node(x);
//tail->_next = tmp;
//tmp->_prev = tail;
//tmp->_next = _head;
//_head->_prev = tmp;
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
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);
}
// 在 pos 位置的前一个插入数据
void insert(iterator pos, const T& val)
{
node* cur = pos._node;
node* prev = cur->_prev;
node* tmp = new node(val);
prev->_next = tmp;
tmp->_prev = prev;
tmp->_next = cur;
cur->_prev = tmp;
}
iterator erase(iterator pos)
{
assert(pos != end());
node* prev = pos._node->_prev;
node* next = pos._node->_next;
delete pos._node;
prev->_next = next;
next->_prev = prev;
return iterator(next);
}
~list<T>()
{
iterator it = begin();
while (it != end())
{
erase(it++);//后置++ 在这里体现作用了,返回it,但是实际上 it 已经++
}
delete _head;
_head = nullptr;
}
private:
node* _head;
};
}