C++ List底层实现


前言

我们都清楚c++中的容器list,本质上就是一个带头双向循环链表,接下来我们实现一下list的底层,帮助我们更深层次的了解list的结构和使用

成员变量

我们知道这个节点有三部分构成 _prev,_next,_val;每个节点看作list的一个元素,这又是一个双向带头循环链表,那么我们如果仅仅知道头节点,就可以访问遍历链表中的所有元素。

//类模板
template < class T>
struct ListNode
{
ListNode(const T& val=T()) //初始化
:_prev(nullptr)
,_next(nullptr)
,_data(val)
{ }
ListNode* _prev;//指向前一个元素
ListNode* _next;//指向后一个元素
T _data;//当前节点的值
};

有了这个结点之后,我们在实现list是创建一个头节点就可以控制这个链表了

template
class list
{
    //模板+类–》类型
    typedef ListNode Node;
private:
    Node* _head;
};

在class类中,默认为私有的,在struct中默认为公有,只有将这个节点定义为公有,我们才可以访问。

那这里采用内部类的方法不行吗??
答案是不行的,如果我们吧struct定义在内部类中,外部list类就无法访问struct中的元素了。

成员函数

迭代器

我们在实现vector中,迭代器我们并没有实现,或者说我们不需要实现,因为vector容容器的结构很特殊,是一块连续的物理空间。加加,解引用等方式很容易实现。

我们看一下迭代器,是一个个地址不连续的指针
在这里插入图片描述

那我们怎末实现呢
我们很清楚迭代器的实质就是一个指针,在这个list中就是哟个listNode的指针。
我们要实现迭代器的操作,可以采用运算符重载的方式实现,但是这里又出现了新的问题,迭代器是一个内置类型,只有自定义类型才可以实现自定义类型重载,所以我们需要都这个指针进行封装,变成一个自定义类型

template< class T >
struct ___list_iterator
{
  typedef ListNode< T> Node;//方便我们使用
  typedef ___list_iterator< T> self;//方便使用,我们会用到
  ___list_iterator(Node* x)//初始化
    :_node(x)
    {}
  Node* _node;//成员变量
};

在这里面实现我们需要的功能,

list::const_iterator it = lt.begin();
while (it != lt.end())
{
std::cout << *it << " ";
++it;
}
std::cout << std::endl;

我们需要实现前置++,后置++,前置- -,后置- -,解引用,判断是否相等。我们依此来看一下

self& operator++()前置++

self& operator++()//++返回的是一个
{
   _node = _node->_next;//我们仅需要让指针往后移动一个节点就可以
  return *this;
}

self operator++(int)后置++

这里只能用self,不能用self&,因为返回的是一个临时对象
self operator++(int)//后置++也是返回一个节点,
{
  self tmp(*this);//首先拷贝一份
  _node = _node->next;//指针向后移动一个节点
  return tmp;//返回拷贝的值
}

self operator–()前置–

self& operator–()
{
   _node = _node->_prev;
   return *this;
}

self operator–(int)后置–

self operator–(int)
{
  self tmp(*this);
   _node = _node->perv;
   return tmp;
}

bool operator!=(const self & tmp)判断是否相等

bool operator!=(const self & tmp)
{
   return _node != tmp._node;//判断两个指针是否指向同一块空间就可以
}

T* operator*() 解引用操作

T* operator*()
{
   return _node->_data;
}

list()初始化

list()
{
   我们初始化仅仅初始化头节点就可以
   empty_list();//我们可以通过调用这个函数创建头节点
}
void empty_list()
{
   _head = new Node;
   _head->_next = _head;
   _head->_prev = _head;
}

iterator begin()

iterator begin()
{
   return _head->_next;//第一个元素就是头节点后面的那个元素
}

iterator end()

iterator end()
{
   return _head;//最后一个元素的下一个就是head
}

const_iterator begin()const

const_iterator begin()const
{
   return _head->_next;
}

const_iterator end()const

const_iterator end()const
{
   return _head;
}

iterator insert(iterator pos, const T& val)在pos位置插入val

iterator insert(iterator pos, const T& val)
{
   Node* cur = pos._node;//我们首先需要取到迭代器中的元素
   Node* newnode = new Node(val);//创建新节点
   Node* prev = cur->_prev;//保留之前的节点
   prev->_next = newnode;//改变指向
   newnode->_prev = prev;
   newnode->_next = cur;
   cur->_prev = newnode;
   return newnode;//返回新节点
}

在这里插入图片描述
我们来看一下这里的insert有没有迭代器失效的问题??

void push_back(const T& val)在尾部位置后插入

void push_back(const T& val)
{
   insert(end(), val);//我们可以直接进行复用
}

void push_front(const T& val)在头部位置插入

void push_front(const T& val)
{
   insert(begin(), val);
}

iterator erase(iterator pos)

iterator erase(iterator pos)
{
   assert(pos != end());//断言一下,不是删除头节点
   Node* cur = pos._node;//取到迭代器中的元素
   Node* prev = cur->_prev;//记录前一个位置
   Node* next = cur->_next;//记录后一个位置
   delete cur;//释放当前元素
   prev->_next = next;//改变指向
   next->_prev = prev;
   return next;//返回删除元素的下一个位置
}

我们来看一下这里会不会有迭代器失效的问题??
我们发现这里存在迭代器失效的问题,并且很大,这个元素删除了之后,之后很可能还需要用到这个元素,继续删除,如果不及时更新,就会出现大问题。

void pop_back()//删除最后一个元素

void pop_back()
{
   erase(–end());//我们要注意end是哪个位置
}

void pop_front()删除第一个元素

void pop_front()
{
   erase(begin());
}

list(list& tmp)拷贝构造

在这里我们有很多方式实现,我们来看一种比较简单的
list(list< T>& tmp)
{
   empty_list();//首先创建一个头节点
   //遍历
   for (const auto& e : tmp)
   {
   push_back(e);//取数据依次插入这个新的头结点中
   }
}

void swap(list&tmp)交换两个list

void swap(list< int>&tmp)
{
   std::swap(_head, tmp._head);//我们仅仅改变两个list的指针就可以
}

list< T>& operator=(list< T> it)赋值

list< T>& operator=(list< T> it)
{
   swap(it);
   return *this;
}

我们再来看一下这个过程,我们假设lt1=lt2;
因为list< T> it这里,lt2会产生一份临时拷贝it
在这里插入图片描述
我们swap(it),本质就是将it与lt1的内容进行交换
在这里插入图片描述
it是临时变量,出了作用域就销毁了,我们就完成了赋值任务

int size()判断有多少元素

int size()
{
   int count = 0;//记录一个变量,一个个统计即可
   for (const auto& e : *this)
   {
   count++;
   }
   return count;
}

bool empty() 判断list是否为空

bool empty()
{
   return size() == 0;//我们只需要判断list是否有元素
}

T& front()取首元素

T& front()
{
   return _head->_next->_data;
}

T& back()取尾元素

T& back()
{
   return _head->_prev->_data;
}

void clear()清空元素

一个个删除元素即可
void clear()
{
   iterator it = begin();
   while (it != end())
   {

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lim 鹏哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值