阅读了很多大厂招聘的面经,发现越是受欢迎的企业,越重视程序员对代码底层实现的考察.所以笔者决定从C++ STL内置容器开始,逐个分析其底层实现逻辑和源码,从而对编程有更为深刻的认识.之前的每次阅读源码的尝试,都因为源码内容多而杂,变量命名怪异而且繁多等等原因而放弃,希望这次我能坚持下来.本次分析的是C++ STL list的源码.
首先要了解list的本质. list是一个双向链表,但也有循环列表的部分性质,这意味着它从任意位置存取元素的速度会比较快,但是随机查询某个位置的元素会比具有一段连续空间的顺序表vector慢.阅读list源码的本质就是弄明白三件事:1.list数据结构 2.迭代器的定义,特别是重载运算符的操作.3.list操作方法的实现.
list数据结构
首先是list节点的定义部分.
1 | struct _List_node_base //list节点的基础实现 |
从这里就可以看出list的双向链表本质了。这一步先是定义了list的基类_List_node_base,实现了前驱和后继指针的定义。然后根据该基类继承实现了_List_node类,在两个指针的基础上,定义了本节点存储的值value。
需要注意其中的一些语法:
1 | template<typename _Tp> |
这是一个类模板,由于用户定义的list可能存取任何可能性的变量类型,所以使用template定义一个类模板,根据此类模板传入value的值,保证了List定义的一般性.可以看到后面定义了一个_Tp类型的变量_M_data
1 | struct _List_node : public __detail::_List_node_base |
定义了_List_node类,继承了_List_node_base类并使用了__detail命名空间
然后是list的一些定义
1 | template<typename _Tp, typename _Alloc> |
可以看到list除了具有双向链表的性质,也具有循环链表的性质,相当于一个双向循环链表.在其不为空时,头节点不存储数据.
迭代器的定义
由于list是一个链表,并不是一块连续的存储空间顺序存储,所以默认的迭代器是不能实现的,需要重写一个迭代器来满足遍历操作。以下为源码。
1 | template<typename _Tp> |
对*,->,++,–等运算符进行了重载,使其能满足用户的需求.
list操作方法的实现
list的操作方法有很多,这里只摘取部分.
1 | iterator begin() _GLIBCXX_NOEXCEPT |
阅读源码可以看到end指向头节点,begin指向第一个数据节点,begin和end组成一个前闭后开区间。所以,如果判断List是否为空,判断end与begin是否指向同一个结点即可.了解这些特性,List的常用方法就可以逐个实现了,这里不再赘述.