list类
基础介绍
list类
是STL中封装的链表模板类,并且底层实现是以双向链表作为基础进行封装的。在数据结构中,线性存储结构中主要分为顺序表和链表,前者在物理结构上拥有连续的内存空间和地址,在STL中vector
和string
都是使用了这种结构,其最大的特点就是方便进行随机访问并且尾插和尾删都能达到O1的时间复杂度并且使用方便,而链表作为物理结构上内存空间不连续的数据结构,其最大的特点就是方便在任何位置就行插入删除,list
就是建立在此数据结构上封装出的模板类。
常用接口
构造函数
1 | list(); //构造空的list |
迭代器相关
1 | begin(); //返回第一个元素的迭代器 |
容量相关
1 | bool empty() const; //检测list是否为空,是返回true,否则返回false |
增删查改
1 | reference front(); //返回list的第一个节点中值的引用 |
emplace与insert
在STL很多接口中我们都发现有一个emplace()
的接口也是用来进行插入的,那么它与insert()
和push_back()
有什么区别呢?以下用vector
举个例子,从接口调用上就能看出区别。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#include <iostream>
#include <vector>
using namespace std;
int main()
{
struct Foo
{
Foo(int n, double x)
:_n(n)
,_x(x)
{
}
int _n;
double _x;
};
vector<Foo> v;
v.emplace(v.begin(), 42, 3.1416); // 没有临时变量产生
v.insert(v.begin(), Foo(42, 3.1416)); // 需要产生一个临时变量
v.insert(v.begin(), {42, 3.1416}); // 需要产生一个临时变量
for(int i = 0; i < v.size(); i++)
{
cout << v[i]._n << " " << v[i]._x << endl;
}
}
42 3.1416
42 3.1416
42 3.1416
在进行内置类型和拥有单参构造函数的类型的插入时我们只传入一个参数往往很难发现区别,但在我们必须传入多个参数才能进行插入时我们就会发现在使用insert()
这些接口时我们必须插入与容器中元素类型相同的元素才能完成接口调用,这让我们不得不构造一个临时匿名对象,而emplace
则不需要,我们只要按照构造一个对象那样给构造元素的参数即可省去了构造匿名对象的过程。而这其中需要用到C++11
中的新标准变参模板和完美转发,因此emplace
使用的时候要求编译器支持C++11
标准。
emplace
不光是调用接口上有所不同,在有的时候也可以提高我们的效率。例如在list
中如果利用insert
传入一个元素的时候我们就需要先调用元素的构造函数构造元素,然后再调用Node
的构造函数以及该元素的拷贝构造函数构造一个拥有该元素值的结点,才能进行插入,而emplace
则可以在创建结点时直接利用原本要构造临时元素对象的参数来直接构造结点,并且直接构造结点中的元素对象,相当于少了一次元素的拷贝构造,并且省去了构造临时对象,效率更高。
list迭代器失效
对于list
这种链式结构,在插入后是不会迭代器失效的,因为原先位置的迭代器依旧指向原来的结点,不会因为添加结点导致指向位置的变动。而在删除后迭代器原本指向的结点内存被释放,因此删除后删除结点的迭代器失效,但是其他结点迭代器指向不变因此不会发生迭代器失效。
总结:list只有在删除节点后才会发生迭代器失效,并且只有删除结点的迭代器失效。
实现
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179#include <assert.h>
//定义结点类
template<class T>
struct ListNode
{
ListNode<T>* _prev;
ListNode<T>* _next;
T _data;
ListNode(const T& data = T())
:_prev(nullptr)
,_next(nullptr)
,_data(data)
{}
};
//由于const_iterator与iterator除了在迭代器返回值上不一样外其他要求完全一样
//因此这里要进行实现时可以考虑定义两个类
//但是这里使用一种取巧的方法我们将返回值类型当作模板参数传入模板中
template<class T, class Ref, class Ptr>
struct ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T, Ref, Ptr> Self;
//构造函数
ListIterator(Node* node)
:_node(node)
{}
//operator* 为了让其可以和指针一样使用,返回引用
Ref operator*()
{
return _node->_data;
}
//operator-> 为了让其可以和指针一样使用,返回指针
//这里实际调用it->只能取到数据的指针,所以正常来说得写成it->->
//但是经过编译器优化,只用写it->就可以取到值了
Ptr operator->()
{
return &(_node->_data);
}
Self operator++()
{
_node = _node->_next;
return _node;
}
Self operator++(int)
{
Self temp(_node);
_node = _node->_next;
return temp;
}
Self operator--()
{
_node = _node->_prev;
return _node;
}
Self operator--(int)
{
Self temp(_node);
_node = _node->_prev;
return temp;
}
bool operator!=(Self it)
{
return _node != it._node;
}
Node* _node;
};
//构建一个带头结点双向循环链表来模拟实现list
template<class T>
class List
{
typedef ListNode<T> Node;
public:
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;
//构造函数j
List()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
//拷贝构造
List(const List& list)
:_head(new Node)
{
_head->_next = _head;
_head->_prev = _head;
const_iterator it = list.begin();
while(it != list.end())
{
Push_back(*it);
it++;
}
}
//operator=重载
List& operator=(const List& list)
{
List listTemp = list;
Swap(listTemp);
}
//交换
void Swap(List& list)
{
std::swap(_head, list._head);
}
//析构函数
~List()
{
while(_head->_next != _head)
{
Pop_back();
}
}
iterator begin()
{
return _head->_next;
}
const_iterator begin() const
{
return _head->_next;
}
iterator end()
{
return _head;
}
const_iterator end() const
{
return _head;
}
iterator Erase(iterator& it)
{
assert(it._node != nullptr);
Node* pTemp = it._node;
Node* pNew = pTemp->_next;
//it->_node = pTemp->_next;
pTemp->_prev->_next = pTemp->_next;
pTemp->_next->_prev = pTemp->_prev;
delete pTemp;
return pNew;
}
//插入
iterator Insert(iterator& it, const T& data = T())
{
assert(it._node != nullptr);
Node* newNode = new Node(data);
newNode->_next = it._node;
newNode->_prev = it._node->_prev;
it._node->_prev->_next = newNode;
it._node->_prev = newNode;
return newNode;
}
//尾插
void Push_back(const T& data)
{
assert(_head != nullptr);
Node* tail = _head->_prev;
Node* newNode = new Node(data);
newNode->_next = _head;
newNode->_prev = tail;
tail->_next = newNode;
_head->_prev = newNode;
}
//尾删
void Pop_back()
{
assert(_head != nullptr);
if(_head->_next == _head)
{
return;
}
Node* tail = _head->_prev;
tail->_prev->_next = _head;
_head->_prev = tail->_prev;
delete tail;
}
private:
Node* _head;
};