STL之List

List的好处学过数据结构都知道,List对空间的使用十分精准,绝不浪费,而且插入和移除元素的成本很低

但是如果需要经常访问元素,那么效率可能就不如Vector

List的节点

写过链表的人都知道,List本身和List的节点是不同的结构,需要分开设计

1
2
3
4
5
6
7
8
9
struct _List_node_base {
_List_node_base* _M_next;
_List_node_base* _M_prev;
};

template <class _Tp>
struct _List_node : public _List_node_base {
_Tp _M_data;
};

节点如上,可以看出是一个双向链表

List迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
struct _List_iterator_base {
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef bidirectional_iterator_tag iterator_category;

_List_node_base* _M_node;

_List_iterator_base(_List_node_base* __x) : _M_node(__x) {}
_List_iterator_base() {}

void _M_incr() { _M_node = _M_node->_M_next; } //移动到后一个节点
void _M_decr() { _M_node = _M_node->_M_prev; } //移动到前一个节点

bool operator==(const _List_iterator_base& __x) const {
return _M_node == __x._M_node;
}
bool operator!=(const _List_iterator_base& __x) const {
return _M_node != __x._M_node;
}
};

template<class _Tp, class _Ref, class _Ptr>
struct _List_iterator : public _List_iterator_base {
typedef _List_iterator<_Tp,_Tp&,_Tp*> iterator;
typedef _List_iterator<_Tp,const _Tp&,const _Tp*> const_iterator;
typedef _List_iterator<_Tp,_Ref,_Ptr> _Self;

typedef _Tp value_type;
typedef _Ptr pointer;
typedef _Ref reference;
typedef _List_node<_Tp> _Node;

_List_iterator(_Node* __x) : _List_iterator_base(__x) {}
_List_iterator() {}
_List_iterator(const iterator& __x) : _List_iterator_base(__x._M_node) {}

reference operator*() const { return ((_Node*) _M_node)->_M_data; }

#ifndef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */

//前进
_Self& operator++() {
this->_M_incr();
return *this;
}
_Self operator++(int) {
_Self __tmp = *this;
this->_M_incr();
return __tmp;
}
//后退
_Self& operator--() {
this->_M_decr();
return *this;
}
_Self operator--(int) {
_Self __tmp = *this;
this->_M_decr();
return __tmp;
}
};

可以看出,对运算符的重载依赖于父类的_M_incr()_M_decr()

List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
template <class _Tp, class _Alloc>
class _List_base
{
public:
typedef _Alloc allocator_type;
allocator_type get_allocator() const { return allocator_type(); }

_List_base(const allocator_type&) {
_M_node = _M_get_node();
_M_node->_M_next = _M_node;
_M_node->_M_prev = _M_node;
}
~_List_base() {
clear();
_M_put_node(_M_node);
}

void clear();

protected:
typedef simple_alloc<_List_node<_Tp>, _Alloc> _Alloc_type;
_List_node<_Tp>* _M_get_node() { return _Alloc_type::allocate(1); }
void _M_put_node(_List_node<_Tp>* __p) { _Alloc_type::deallocate(__p, 1); }

protected:
_List_node<_Tp>* _M_node;//只需要一个指针,便可以表示整个环状双向List
};

然后,在class中基本是对_M_node的操作

1
2
3
4
5
iterator begin()             { return (_Node*)(_M_node->_M_next); }
const_iterator begin() const { return (_Node*)(_M_node->_M_next); }

iterator end() { return _M_node; }
const_iterator end() const { return _M_node; }

由上可知,_M_node是最后一个节点,然后前一个是头节点

List对节点的操作

1
2
3
4
void push_front(const _Tp& __x) { insert(begin(), __x); }
void push_front() {insert(begin());}
void push_back(const _Tp& __x) { insert(end(), __x); }
void push_back() {insert(end());}

可以看到,以上操作依赖于insert

1
2
3
4
5
6
7
8
iterator insert(iterator __position, const _Tp& __x) {
_Node* __tmp = _M_create_node(__x);
__tmp->_M_next = __position._M_node;
__tmp->_M_prev = __position._M_node->_M_prev;
__position._M_node->_M_prev->_M_next = __tmp;
__position._M_node->_M_prev = __tmp;
return __tmp;
}