在前面我已经过讲过list的使用,但是如果想进一步提高自己的话,还是要深入了解底层实现的。在这里博主带大家一起走进list的底层实现。当然,我模拟实现的list肯定没有官方实现的优秀,还是那句话,我们不是造更好的轮子,而是帮助我们更深入了解底层~
目录
bool operator != (const self& it) const
bool operator == (const self& it) const
list(size_t n, const T& val = T())
list(int n, const T& val = T())
list(InputIterator first, InputIterator last)
iterator insert(iterator pos, const T& x)
bool operator!=(const self& rit) const
bool operator==(const self& rit) const
成员变量的定义
struct ListNode
template<class T>
struct ListNode:
ListNode<T>* _prev;
ListNode<T>* _next;
T _data;
struct __list_iterator
//封装一个迭代器
template<class T, class Ref, class Ptr>
struct __list_iterator
typedef ListNode<T> Node;
typedef __list_iterator<T, Ref, Ptr> self; //一个迭代器对象
typedef Ref reference;
typedef Ptr pointer;
Node* _node;
class list
template<class T>
class list
typedef ListNode<T> Node;
public:
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator; //第一个T是表示类型,不加const
typedef reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;
typedef reverse_iterator<iterator, T&, T*> reverse_iterator;
private:
Node* _head;
class reverse_iterator
//适配器
//Iterator是哪个容器的迭代器,reverse_iterator<Iterator>就可以
//适配出哪个容器的反向迭代器,复用的体现
template<class Iterator, class Ref, class Ptr>
class reverse_iterator
typedef reverse_iterator<Iterator, Ref, Ptr> self;
private:
Iterator _it;
成员函数
struct ListNode
ListNode(T data = T())
:_prev(nullptr)
,_next(nullptr)
,_data(data)
{}
ListNode(T data = T())
:_prev(nullptr)
,_next(nullptr)
,_data(data)
{}struct ListNode是我们定义一个结点的类,上面展示它的成员变量,在这里构造函数是完成对该类的初始化。这样我们每次new出来的结点,都是初始化过的。(new出来的会自动调用类里面的构造函数.)
![]()
struct __list_iterator
__list_iterator(Node*x)
__list_iterator(Node*x)
:_node(x)
{}
//对迭代器进行封装,这里对其构造函数初始化,
__list_iterator(Node*x)
:_node(x) //将传过来的结点x传给_node。在这里, _node会自动调用它的(struct ListNode类在这里由于没有显示实现拷贝构造,所以会调用编译器默认生成的)拷贝构造函数来完成初始化。
{}
Ref operator*()
Ref operator*()
{
return _node->_data;
}
Ref operator*() //Ref在这里是返回的引用,如果list类里面传过来的T&就返回的是T&;如果是const T&就返回的是const T&。
思考:
为什么要有Ref呢?如果我们只在上面定义template<class T>。
会不会存在这种场景,迭代器里面有写成员可能让我们返回T&,或者指针。如果不使用多个模板参数参数的话,在实现时就会大量的拷贝,有很多重复的地方(在内部基本上对某些要求的成员函数返回值进行修改:T& ,const T&...)。所以我们在这里实现多个模板参数,这样返回值就可以有更多的自主选择来满足我们的需求。
{
return _node->_data;
}
Ptr operator->()
Ptr operator->()
{
return &_node->_data;
}
Ptr operator->() //Ptr和上面Ref类似,只不过Ptr代表是返回指针。
{
return &_node->_data;/返回结点里面数据(T)的地址
}我们应该这样使用:
p.operator->() / p->operator->();//不知道内部成员变量名的话就这么用,不用显示调p->_data(库里面不一定是_data)
self& operator++()
//++it
self& operator++()
{
_node = _node->_next;
return *this;
}
//++it
self& operator++() //正向迭代器,前置++ 往后迭代。
{
_node = _node->_next; //往后迭代
return *this; //返回++后的对象self。self:typedef __list_iterator<T, Ref, Ptr> self;
}
self operator++(int)
//it++
self operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
//it++ 后置++
self operator++(int) //int是表示占位,是为了与前置++进行函数重载。
{
self tmp(*this); //先拷贝构造一个临时对象,方便返回。以免++后,就找不到++前的结点了。——> 在这里有一个拷贝构造,所以我们尽量使用前置++,代码效率会更高。
_node = _node->_next;
return tmp; //返回tmp对象,因为这是后置++,++后的结果在这里不返回。tmp是临时对象,所以上面不能传引用返回!
}
self& operator--()
//--it
self& operator--()
{
_node = _node->_prev;
return *this;
}
//--it 前置--
self& operator--()
{
_node = _node->_prev; //往前迭代。
return *this; //返回迭代后的对象。
}
self operator--(int)
//it--
self operator--(int)
{
self tmp(*this); //先构造一个临时对象
_node = _node->_prev;
return tmp;
}
//it--
self operator--(int)
{
self tmp(*this); //先构造一个临时对象。同理,后置--涉及一次拷贝构造,所以前置--效率比后置--效率高。
_node = _node->_prev;
return tmp; //tmp是临时对象,在这里不能传引用返回。
}
bool operator != (const self& it) const
bool operator != (const self& it) const
{
return _node != it._node;
}
bool operator == (const self& it) const
bool operator == (const self& it) const
{
return _node == it._node;
}
上面的成员函数,和string、vector基本类似,在之前的博客已经详细介绍过了,在这里我就不再赘述了~
class list
iterator begin()
iterator begin()
{
return iterator(_head->_next); //Node*
}
iterator begin()
{
return iterator(_head->_next); //调用正向迭代器里面的构造函数,在这里返回的是匿名对象。我们发现匿名对象有时候用起来也是很爽的。
}
iterator end()
iterator end()
{
return iterator(_head); //Node*
}
const_iterator begin() const
const_iterator begin() const //不加const会冲突,下面也一样
{
return const_iterator(_head->_next);
}
const_iterator end() const
const_iterator end() const
{
return const_iterator(_head);
}
reverse_iterator rbegin()
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rbegin()
{
return reverse_iterator(end()); //调用了反向迭代器的构造函数,在这里rbegin返回的是构造的匿名对象。我们发现匿名对象有时候用起来也是很爽的。在这里rbegin()指向的是哨兵位头结点。
}
reverse_iterator rend()
reverse_iterator rend()
{
return reverse_iterator(begin());
}
还是一样,上面很多都是在之前讲过,在这里我们用之前画的图来表示一下begin()、rbegin()、end()、rend()之间的关系:
list()
list() //初始化,构造一个头结点
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
}
list() //初始化,构造一个头结点
{
_head = new Node(); //初始化头结点里指针的关系。
_head->_next = _head;
_head->_prev = _head;
}
list(size_t n, const T& val = T())
list(size_t n, const T& val = T())
{
_head = new Node; //加不加括号都可以
_head->_next = _head;
_head->_prev = _head;
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
list(size_t n, const T& val = T())
{
_head = new Node; //加不加括号都可以
_head->_next = _head;
_head->_prev = _head;//这个也是构造函数,我们第一步要主动初始化哨兵位头结点。
for (size_t i = 0; i < n; i++) //循环n次,把n个val数据一次插入
{
push_back(val); //调用push_back()接口进行尾插。
}
}
list(int n, const T& val = T())
list(int n, const T& val = T())
{
_head = new Node; //加不加括号都可以
_head->_next = _head;
_head->_prev = _head;
for (int i = 0; i < n; i++)
{
push_back(val);
}
}
list(int n, const T& val = T()):这个函数和上面的函数构成函数重载,在这里实现这个版本是为了解决一个bug。
list(InputIterator first, InputIterator last)
template<class InputIterator>
list(InputIterator first, InputIterator last)
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
while (first != last)
{
push_back(*first);
first++;
}
}
template<class InputIterator> //普通迭代器
list(InputIterator first, InputIterator last) //迭代器区间进行初始化。
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;//这个也是构造函数,我们第一步要主动初始化哨兵位头结点。
while (first != last) //last表示最后一个数据的下一个位置的迭代器。
{
push_back(*first); //*first,拿到迭代器里面结点中的数据。*是重载过的。
first++;
}
}
list(const list<T>& lt)
list(const list<T>& lt)
{
//先初始化
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
list<T> tmp(lt.begin(), lt.end()); //复用构造函数
std::swap(_head, tmp._head);
}
list(const list<T>& lt)
{
//先初始化
_head = new Node();
_head->_next = _head;
_head->_prev = _head;//拷贝构造也是构造函数,调用拷贝构造构造函数就不会主动调用了
list<T> tmp(lt.begin(), lt.end()); //复用构造函数
std::swap(_head, tmp._head); //进行资源的交换,实际上就是交换头结点。
}//构造的临时对象tmp,会自动调用析构函数,完成资源的清理。
方法二:传统写法
//lt2(lt1)
list(const list<T>& lt)
{
//先初始化
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
for (auto e : it)
{
push_back(e);
}
}
list<T>& operator=(list<T> lt)
list<T>& operator=(list<T> lt) //复用拷贝构造
{
std::swap(_head, lt._head);
return *this;
}
list<T>& operator=(list<T> lt) //复用拷贝构造
{
std::swap(_head, lt._head); //交换头结点,这时候*this就被赋值好了
return *this; //返回被赋值好的对象
} //lt是临时对象,这时候lt自动调用析构函数完成对原*this里的资源清理。
方法二:传统写法:
//lt2=lt1
list<T>& operator=(list<T>& lt) //复用拷贝构造
{
if (this != <) //不能上来就清空!!
{
clear();
for (auto e : lt)
{
push_back(e);
}
}
return *this;
}
//lt2=lt1
list<T>& operator=(list<T>& lt) //传引用
{
if (this != <) //不能上来就清空!!这里不能给自己赋值
{
clear(); //清空自己里面的结点,只剩下一个头结点。
for (auto e : lt) //依次插入
{
push_back(e);
}
}
return *this;
}
~list()
~list()
{
clear();
delete _head;
_head = nullptr;
}
~list()
{
clear(); //调用clear()清空里面的结点,只剩下一个头结点。
delete _head; //释放头结点
_head = nullptr; //头结点置空。
}
void clear()
void clear()
{
iterator it = begin();
while (it != end())
{
iterator del = it++;
delete del._node; //_node是在list类中new出来的
}
//清理完节点后重新链接头结点关系
_head->_next = _head;
_head->_prev = _head;
//iterator it = begin();
//while (it != end())
//{
// erase(it++); //这里不能分开写
//}
}
void clear()
{
iterator it = begin();
while (it != end())
{
iterator del = it++; //提前记录要删除的节点的迭代器
delete del._node; //_node是在list类中new出来的,不是delete del
}
//清理完节点后重新链接头结点关系
_head->_next = _head;
_head->_prev = _head;//方法二:
//iterator it = begin();
//while (it != end())
//{
// erase(it++); //这里不能分开写 这里先返回it++前的临时对象。
//}
}
void push_front(const T& x)
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
void pop_front()
{
erase(begin());
}
void push_back(const T& x)
void push_back(const T& x)
{
insert(end(), x);//end()不能给成_head,这里是一个迭代器
}
void pop_back()
void pop_back()
{
erase(--end());
}
void pop_back()
{
erase(--end()); //end()是哨兵位头结点位置的迭代器,--end()就是哨兵位前一个节点的迭代器,也就是最后一个数据位置的迭代器。
}
iterator insert(iterator pos, const T& x)
iterator insert(iterator pos, const T& x)
{
Node* prev = pos._node->_prev;
Node* cur = pos._node;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
return iterator(newnode);
}
iterator insert(iterator pos, const T& x)
{//这里一定注意是结点来连接关系,不使用迭代器。
Node* prev = pos._node->_prev; //prev指向pos迭代器里结点的前一个结点的位置。
Node* cur = pos._node;//cur指向当前迭代器里的结点
Node* newnode = new Node(x); //构造新的结点prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
return iterator(newnode); //iterator(newnode)构造匿名迭代器对象并返回。
}
iterator erase(iterator pos)
//erase之后,it就失效了
iterator erase(iterator pos)
{
assert(pos != end()); //头结点不能删
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
return iterator(next);
}
//erase之后,it就失效了
iterator erase(iterator pos)
{
assert(pos != end()); //头结点不能删
Node* prev = pos._node->_prev; //这里一定注意是结点来连接关系,不使用迭代器。
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;delete pos._node;
return iterator(next); //返回删除结点的下一个结点位置的迭代器。
}
class reverse_iterator
reverse_iterator(Iterator it)
reverse_iterator(Iterator it)
:_it(it)
{}
//Iterator是哪个容器的迭代器,reverse_iterator<Iterator>就可以适配出哪个容器的反向迭代器,复用的体现
reverse_iterator(Iterator it) //Iterator可以说是容器适配器。
:_it(it)
{}
Ref operator*()
Ref operator*()
{
Iterator prev = _it;
return *--prev;
}
Ref operator*()
{
Iterator prev = _it; //_it是构造好出来的Iterator对象。为了防止_it被修改掉,我们用prev来提前记录一下。
return *--prev; //这里解引用我们发现并不是直接解引用,而是解引用当前迭代器的前一个迭代器。
}
方法二:
// 1、这时候Iterator还没有实例化,还没法使用::,这时候需要使用typename提前告知有这个类型
typename Iterator::reference operator*()
{
Iterator prev = _it;
return *--prev;
}
Ptr operator->()
Ptr operator->()
{
return &operator*();
}
Ptr operator->()
{
return &operator*(); //返回*结点数据的地址
}
方法二:
// 1、这时候Iterator还没有实例化,还没法使用::,这时候需要使用typename提前告知有这个类型
typename Iterator::pointer operator->()
{
return &operator*();
}
self& operator++()
self& operator++()
{
--_it;
return *this;
}
self& operator++()
{
--_it; //反向迭代器的++对应正向迭代器的--,实际上这里又调用了正向迭代器里的operator--
return *this;
}
self operator++(int)
self operator++(int)
{
self tmp(*this);
--_it;
return tmp;
}
self& operator--()
self& operator--()
{
++_it;
return *this;
}
self& operator--()
{
++_it; //反向迭代器的--对应正向迭代器的++,实际上这里又调用了正向迭代器里的operator++
return *this;
}
self operator--(int)
self operator--(int)
{
self tmp(*this);
++_it;
return tmp;
}
bool operator!=(const self& rit) const
bool operator!=(const self& rit) const
{
return _it != rit._it;
}
bool operator==(const self& rit) const
bool operator==(const self& rit) const
{
return _it == rit._it;
}
上面的成员函数接口都是类似,之前都有很详细的介绍,在这里我就不再赘述了,如果有不懂得地方,或者我有错误的地方可以来私信我。
完整代码
list.h
#pragma once
#include<iostream>
#include<assert.h>
#include"reverse_iterator.h"
using namespace std;
namespace cyq
{
template<class T>
struct ListNode
{
ListNode<T>* _prev;
ListNode<T>* _next;
T _data;
ListNode(T data = T())
:_prev(nullptr)
,_next(nullptr)
,_data(data)
{}
};
//封装一个迭代器
template<class T, class Ref, class Ptr>
struct __list_iterator
{
typedef ListNode<T> Node;
typedef __list_iterator<T, Ref, Ptr> self; //一个迭代器对象
typedef Ref reference;
typedef Ptr pointer;
Node* _node;
//it2 = it1 浅拷贝 -->假设是深拷贝,这从某种意义上又搞了一个链表,这样就乱套了
//拷贝构造和赋值重载不需要我们自己实现
//析构呢? ->迭代器是借助结点的指针访问修改链表
//结点属于链表,不属于迭代器,所以这里不用管释放
__list_iterator(Node*x)
:_node(x)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
//++it
self& operator++()
{
_node = _node->_next;
return *this;
}
//it++
self operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
//--it
self& operator--()
{
_node = _node->_prev;
return *this;
}
//it--
self operator--(int)
{
self tmp(*this); //先构造一个临时对象
_node = _node->_prev;
return tmp;
}
//重载两个迭代器的比较
bool operator != (const self& it) const
{
return _node != it._node;
}
bool operator == (const self& it) const
{
return _node == it._node;
}
};
template<class T>
class list
{
typedef ListNode<T> Node;
public:
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator; //第一个T是表示类型,不加const
typedef reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;
typedef reverse_iterator<iterator, T&, T*> reverse_iterator;
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
iterator begin()
{
return iterator(_head->_next); //Node*
}
iterator end()
{
return iterator(_head); //Node*
}
const_iterator begin() const //不加const会冲突,下面也一样
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
list() //初始化,构造一个头结点
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
}
//<Date> <int> 决定了后面数据类型
// list<Date> lt1(5, Date(2022,4,12));
// list<int> lt2(5, 1);
//如果只实现一个size_t的版本上面两个可能存在参数匹配问题。
// (5, Date(2022,4,12)) int/size_t Date
// list<int> lt2(5, 1) int int比(size_t int)更匹配,于是走InputIterator那个地方了,
//整数1被解引用后就会报错
list(size_t n, const T& val = T())
{
_head = new Node; //加不加括号都可以
_head->_next = _head;
_head->_prev = _head;
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
list(int n, const T& val = T())
{
_head = new Node; //加不加括号都可以
_head->_next = _head;
_head->_prev = _head;
for (int i = 0; i < n; i++)
{
push_back(val);
}
}
template<class InputIterator>
list(InputIterator first, InputIterator last)
{
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
while (first != last)
{
push_back(*first);
first++;
}
}
//lt2(lt1)
//拷贝构造也是构造函数,调用拷贝构造构造函数就不会主动调用了
list(const list<T>& lt)
{
//先初始化
_head = new Node();
_head->_next = _head;
_head->_prev = _head;
list<T> tmp(lt.begin(), lt.end()); //复用构造函数
std::swap(_head, tmp._head);
}
//lt2=lt1
list<T>& operator=(list<T> lt) //复用拷贝构造
{
std::swap(_head, lt._head);
return *this;
}
//方法二:传统写法
//lt2(lt1)
//list(const list<T>& lt)
//{
// //先初始化
// _head = new Node();
// _head->_next = _head;
// _head->_prev = _head;
//
// for (auto e : it)
// {
// push_back(e);
// }
//}
lt2=lt1
//list<T>& operator=(list<T>& lt)
//{
// if (this != <) //不能上来就清空!!
// {
// clear();
// for (auto e : lt)
// {
// push_back(e);
// }
// }
// return *this;
//}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
iterator del = it++;
delete del._node; //_node是在list类中new出来的
}
//清理完节点后重新链接头结点关系
_head->_next = _head;
_head->_prev = _head;
//iterator it = begin();
//while (it != end())
//{
// erase(it++); //这里不能分开写
//}
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
void push_back(const T& x)
{
insert(end(), x);//end()不能给成_head,这里是一个迭代器
}
void pop_back()
{
erase(--end());
}
iterator insert(iterator pos, const T& x)
{
Node* prev = pos._node->_prev;
Node* cur = pos._node;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
return iterator(newnode);
}
//erase之后,it就失效了
iterator erase(iterator pos)
{
assert(pos != end()); //头结点不能删
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
return iterator(next);
}
private:
Node* _head;
};
}
reverse_iterator.h
#pragma once
namespace cyq
{
//适配器
//Iterator是哪个容器的迭代器,reverse_iterator<Iterator>就可以
//适配出哪个容器的反向迭代器,复用的体现
template<class Iterator,class Ref,class Ptr>
class reverse_iterator
{
typedef reverse_iterator<Iterator, Ref, Ptr> self;
public:
reverse_iterator(Iterator it)
:_it(it)
{}
// 1、这时候Iterator还没有实例化,还没法使用::,这时候需要使用typename提前告知有这个类型
typename Iterator::reference operator*()
{
Iterator prev = _it;
return *--prev;
}
//2、
//Ref operator*()
//{
// Iterator prev = _it;
// return *--prev;
//}
// 1、这时候Iterator还没有实例化,还没法使用::,这时候需要使用typename提前告知有这个类型
typename Iterator::pointer operator->()
{
return &operator*();
}
//2、
/*Ptr operator->()
{
return &operator*();
}*/
self& operator++()
{
--_it;
return *this;
}
self operator++(int)
{
self tmp(*this);
--_it;
return tmp;
}
self& operator--()
{
++_it;
return *this;
}
self operator--(int)
{
self tmp(*this);
++_it;
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;
};
}
测试代码
struct Date
{
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
int _year;
int _month;
int _day;
};
void test_list()
{
cyq::list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_front(10);
lt.push_front(20);
lt.push_front(30);
cyq::list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
it++;
}
cout << endl;
lt.pop_back();
lt.pop_front();
it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
it++;
}
cout << endl;
cyq::list<int> lt2(5, 10);
for (auto e : lt2)
{
cout << e << " ";
}
cout << endl;
cyq::list<Date> lt3(5, Date(2022,4,13));
auto it3 = lt3.begin();
while (it3 != lt3.end())
{
cout << it3->_year << "/" << it3->_month << "/" << it3->_day << endl;
it3++;
}
//for (auto e : lt3)
//{
// cout << e << endl;
//}
cout << endl;
auto it4 = lt2.rbegin();
while (it4 != lt2.rend())
{
cout << *it4 << " ";
it4++;
}
cout << endl;
}
int main()
{
test_list();
return 0;
}
测试结果:
博主觉得之前的模拟实现容器的博客和这篇博客很相似,所以有些地方写的比较简略,相信大家也能看懂的~
如果大家又不懂得地方欢迎来私信我~
list模拟实现难点主要是容器适配器和类与类之间的反复调用,如果大家看得有些吃力,可能C++类和对象的知识有些不足,老铁们可以看一下我曾经写的关于类和对象的博客。
看到这里,希望大家支持下~