目录
- list的实现结构
- 节点的实现
- 迭代器的实现
- 第一个模板参数T
- 第二个模板参数Ref
- 第三个模板参数Ptr
- 实现list中的接口函数
- 插入和删除
- 赋值重载和拷贝构造
- 析构函数
- 总结
list的实现结构
STL库中的list的结构是双向循环链表,所以我们这里也实现一个双向循环链表
我们这里采取带头双向循环链表
为了方便节点间的操作,可以将节点封装成一个结构体
list中的迭代器是双向迭代器,只支持++
和--
操作,不支持+
,-
,所以不能用一个指针用作迭代器,所以还需要将迭代器封装成一个结构体,在结构体中对迭代器的一些操作进行实现
节点的实现
template<class T>
struct __list_node
{
__list_node<T>* _next;
__list_node<T>* _prev;
T _val;
__list_node(const T& val = T())
:_next(nullptr)
,_prev(nullptr)
,_val(val)
{}
};
主义,这是一个类模板,类名是__list_node
,类型是__list_node<T>
所以2个指针_next
,_prev
的类型是__list_node<T>
,分别指向前一个节点和后一个节点
现在,我们就可以把头节点作在为ist类中的成员变量
节点的类型为__list_node<T>
,比较麻烦,这里我们可以 typedef __list_node<T> Node
因为在类外我们不会直接用到Node
,所以把typedef __list_node<T> Node
放到private中就可以
同时实现构造函数,让_head
的_next
指向自己,_prev
也指向自己
template<class T >
class list
{
typedef __list_node<T> Node;
public:
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
private:
Node* _head;
};
迭代器的实现
list的迭代器是双向迭代器,只支持++
和--
操作,不支持+
,-
所以不能只是简单的服用指针
要对指针进行封装,在结构体中实现++
,--
,*
,->
等操作
第一个模板参数T
list迭代器的底层其实还是Node*
类型的指针
template<class T>
struct __list__iterator
{
typedef __list_node<T> Node;
typedef __list__iterator<T> Iter;
__list__iterator(Node* pnode)
:_pnode(pnode)
{}
Node* _pnode;
};
因为总使用
__list_node<T>
和__list__iterator<T>
比较麻烦,所以typedef __list_node<T> Node; typedef __list__iterator<T> Iter;
++
操作实际上的底层是当前迭代器指向当前指向节点的下一个节点
--
操作实际上的底层是当前迭代器指向当前指向节点的前一个节点
所以下面就可以实习++
,--
操作
Iter& operator++()
{
_pnode = _pnode->_next;
return *this;
}
Iter operator++(int)
{
Iter tmp(*this);
_pnode = _pnode->_next;
return tmp;
}
Iter& operator--()
{
_pnode = _pnode->_prev;
return *this;
}
Iter operator--(int)
{
Iter tmp(*this);
_pnode = _pnode->_prev;
return tmp;
}
解引用操作也就是返回迭代器指向节点中的值
T& operator*()
{
return _pnode->_val;
}
T* operator->()
{
return &_pnode->_val;
}
!=
和==
就是比较迭代器下面的指针
bool operator!=(const Iter it)
{
return _pnode != it._pnode;
}
bool operator==(const Iter it)
{
return _pnode == it._pnode;
}
接下来在list
中实现begin()
和end()
函数
__list__iterator<T>
麻烦,typedef成iterator
typedef __list__iterator<T> iterator;
我们需要在类外使用到iterator
,所以这句typedef
要放到public中
list的结构如下:
因为begin()
返回的是指向第一个有效节点的迭代器,所以_head->_next
就是第一个有效节点
iterator begin()
{
return iterator(_head->_next);
}
end()
返回的是最后一个节点的下一个位置,这里最后一个节点的下一个位置就是_head
iterator end()
{
return iterator(_head);
}
第二个模板参数Ref
现在有一个问题,就是如何实现const_iterator
,在前面iterator
的基础上,我们可以在封装一个const_iterator
的结构体,其中的返回值改为const T&
或const T*
但是再封装一个,就会导致代码冗余
这里我们引入第二个模板参数class Ref
这个参数是用来接收返回引用的类型的可以是T&
也可以是const T&
所以这时的*
重载返回值就是Ref
了
Ref operator*()
{
return _pnode->_val;
}
第三个模板参数Ptr
同理,对于->
重载的返回类型是指针
引入第三个模板参数Ptr
它是用来接收T*
和const T*
的
Ptr operator->()
{
return &_pnode->_val;
}
现在就可以在list中实现cbegin
和cend1
了
首先还是将typedef
出iterator
和const_iterator
typedef __list__iterator<T, T&, T*> iterator;
typedef __list__iterator<T, const T&, const T*> const_iterator;
const_iterator begin()const
{
return const_iterator(_head->_next);
}
const_iterator end()const
{
return const_iterator(_head);
}
实现list中的接口函数
插入和删除
这里的insert
和erase
的实现和C语言中实现链表的操作一样,比较容易
这里唯一需要注意的是:insert
函数返回插入新节点的迭代器,erase
函数返回删除节点后,删除节点下一个节点位置的迭代器
iterator insert(iterator it, const T& val)
{
Node* newnode = new Node(val);
Node* cur = it._pnode;
Node* prev = it._pnode->_prev;
newnode->_next = cur;
cur->_prev = newnode;
prev->_next = newnode;
newnode->_prev = prev;
_size++;
return newnode;
}
iterator erase(iterator it)
{
assert(it != _head);//需要检查一下it是否是头节点位置的迭代器,头节点不能删除
Node* del = it._pnode;
Node* prev = it._pnode->_prev;
Node* nex = it._pnode->_next;
prev->_next = nex;
nex->_prev = prev;
_size--;
delete del;
return nex;
}
实现了insert
和erase
后,通过复用这两个函数,就可以实现push_back
,push_front
,pop_back
,pop_front
函数了
void push_back(const T& val)
{
insert(end(), val);
}
void push_front(const T& val)
{
insert(begin(), val);
}
void pop_back()
{
erase(_head->_prev);
}
void pop_front()
{
erase(_head->_next);
}
赋值重载和拷贝构造
赋值重载的实现,可以使用现代写法,和在vector
中赋值重载的写法类似,使用swap
void swap(list<T> lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
//赋值重载现代写法
list<T> operator=(list<T> lt)
{
swap(lt);
return *this;
}
拷贝构造,我们先构造出一个list,然后用push_back
向里面插入节点
//拷贝构造
list(const list<T>& lt)
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
for (auto& e : lt)
{
push_back(e);
}
}
这里有一点:
库中复制拷贝和运算符重载中参数列表中形参类型不是list<T>
,而是list类型没有指定后面的模板参数
析构函数
析构函数需要清理空间,对于链表来说,我们不能只清理头节点
我们需要清理掉有有效节点后,最后清理掉头节点
这里我们定义一个清理掉有有效节点的函数
使用erase
+循环清理节点
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
_size = 0;
}
然后在销毁函数中调用clear
函数,再清理掉头节点
~list()
{
clear();
delete _head;
_head = nullptr;
}
总结
此时,list的模拟实现就基本完成了
- list的模拟实现中,有难度的地方就是把指针封装成迭代器,在封装中实现以及限制了迭代器的作用
- 对于复杂类型,我们可以
typedef
出一个简单的别名,对于这个别名,如果要在类外使用就定义在public里,如果不允许在类外使用,就定义在private中
完整代码:
namespace my_list
{
template<class T>
struct __list_node
{
__list_node<T>* _next;
__list_node<T>* _prev;
T _val;
__list_node(const T& val = T())
:_next(nullptr)
, _prev(nullptr)
, _val(val)
{}
};
//typedef __list__iterator<T,T&,T*> iterator;
//typedef __list__iterator<T, const 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> Iter;
__list__iterator(Node* pnode)
:_pnode(pnode)
{}
Node* _pnode;
bool operator!=(const Iter it)
{
return _pnode != it._pnode;
}
bool operator==(const Iter it)
{
return _pnode == it._pnode;
}
Iter& operator++()
{
_pnode = _pnode->_next;
return *this;
}
Iter operator++(int)
{
Iter tmp(*this);
_pnode = _pnode->_next;
return tmp;
}
Iter& operator--()
{
_pnode = _pnode->_prev;
return *this;
}
Iter operator--(int)
{
Iter tmp(*this);
_pnode = _pnode->_prev;
return tmp;
}
Ref operator*()
{
return _pnode->_val;
}
Ptr operator->()
{
return &_pnode->_val;
}
};
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()
{
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);
}
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
empty_init();
}
list(size_t n, const T& val = T())
{
empty_init();
for (int i = 0; i < n; i++)
{
push_back(val);
}
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
//拷贝构造
list(const list<T>& lt)
//list(const list& lt) //库中的拷贝构造是这么定义的,list类型没有指定后面的模板参数
{
empty_init();
for (auto& e : lt)
{
push_back(e);
}
}
void swap(list<T> lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
//赋值重载现代写法
list<T> operator=(list<T> lt)
//list operator=(list lt) //库中的赋值重载是这么定义的,list类型没有指定后面的模板参数
{
swap(lt);
return *this;
}
size_t size() const
{
return _size;
}
bool empty() const
{
if (_head->_next == _head || _head->_prev == _head)
return true;
else
return false;
}
iterator insert(iterator it, const T& val)
{
Node* newnode = new Node(val);
Node* cur = it._pnode;
Node* prev = it._pnode->_prev;
newnode->_next = cur;
cur->_prev = newnode;
prev->_next = newnode;
newnode->_prev = prev;
_size++;
return newnode;
}
iterator erase(iterator it)
{
assert(it != _head);//需要检查一下it是否是头节点位置的迭代器,头节点不能删除
Node* del = it._pnode;
Node* prev = it._pnode->_prev;
Node* nex = it._pnode->_next;
prev->_next = nex;
nex->_prev = prev;
_size--;
delete del;
return nex;
}
void push_back(const T& val)
{
insert(end(), val);
}
void push_front(const T& val)
{
insert(begin(), val);
}
void pop_back()
{
erase(_head->_prev);
}
void pop_front()
{
erase(_head->_next);
}
void print()
{
iterator it = begin();
while (it != end())
{
std::cout << *it << " ";
it++;
}
std::cout << std::endl;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);//经过erase后的迭代器会失效,为了让循环继续下去,用it接收erase返回的迭代器(删除位置迭代器的下一个位置的迭代器)
}
_size = 0;
}
private:
Node* _head;
size_t _size; //加一个_size成员变量,如果不设置_size,在实现size()函数时就需要遍历list,时间复杂度为O(n)
};
}