- 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
这样list的基本框架就搭建好了!!
二、节点的尾插和头插
2.1 节点的头插 (push_back)

通过前面数据结构的学习。我们已经清楚了链表的结构,在进行数据尾插时, 就只是在改变指针的指向罢了:
void push_back(const T& val)
{
node* creat = new node(val);
node* tail = _head->_prev;
tail->_next = creat;
creat->_prev = tail;
creat->_next = _head;
_head->_prev = creat;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
2.2 节点的头插(push_front)
节点的头插和尾插十分的相似,
这里我们就直接展示一下代码:
void push_front(const T& val)
{
node* fnode = new node(val);
fnode->_next = _head->_next;
fnode->_prev = _head;
_head->_next->_prev = fnode;
_head->_next = fnode;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
2.3 数据插入验证
为了方便起见, 我这里再再这个类里面定义一个打印链表的函数:
void Print_list()
{
node* cur = _head->_next;
while (cur != _head)
{
cout << cur->_data << " ";
cur = cur->_next;
}
cout << endl;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
完成了list容器的头插和尾插操作接下来我们可以来验证一下,我们的函数实现是否有问题:
#include"list.h"
int main()
{
zdl::list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_front(3);
l1.Print_list();
return 0;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
通过运行可知, 这里的函数没有问题:

三、list迭代器的实现
list迭代器的功能和vetor的功能有很多相似的地方,但是二者在底层实现时,使用的不同的方法:
基于,list迭代器的特殊性质,我们会采用类封装的方式来满足list_iterator的特殊性质:
3.1迭代器基本功能的实现和封装处理
template<class T>
struct list_iterator
{
typedef ListNode<T> node;
typedef list_iterator<T> self;
node* _nd;
list_iterator(node* nd)
:_nd(nd)
{}
T& operator*()
{
return _nd->_data;
}
self& operator++()
{
_nd = _nd->_next;
return *this;
}
self& operator--()
{
_nd = _nd->_prev;
return *this;
}
self operator++(int)
{
self tmp = _nd;
_nd = _nd->_next;
return tmp;
}
self operator--(int)
{
self tmp = _nd;
_nd = _nd->_prev;
return tmp;
}
bool operator!=(const self& s)
{
return _nd != s._nd;
}
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 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
实现了list_iterator的功能后,我们就只需要将他封装道list类中就可以正常使用了:


基本的逻辑都是十分的简单的接下来我们来简单的验证一代码是否可行:

3.2 对结构体(类)的解引用
对 ->
运算符的重载
大家现在可以想象一下这样的场景,假设我现在在llist
中存储的不是基础类型的元素,而是类等较为复杂的对象时,我们应当怎么正常的使用这个类对象的成员呢?
这时,我们就可以考虑对->
运算符重载, 通过返回对象的地址,来访问成员变量和成员函数等!
下面我们就来举个例子:
假设我现在我要在list
中存储pos
类:
struct pos
{
int row;
int col;
pos(const int& x = 0, const int& y = 0)
:row(x)
,col(y)
{}
void pos_show()
{
printf("(%d, %d) ",row, col);
}
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
我们就只需要重载->
运算符:
T* operator->()
{
return &_nd->_data;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
运行后可以得到:

但是大家可能会觉得很奇怪,通过->
重载,返回的只是_data
的地址,为什么能直接访问到元素呢?不应该使用it->->
解引用两次才行啊?
这个其实就只是c++
便准下为我们提供的特殊语法, 通过这样的规定使我们能够直接访问到元素, 当然c++
也是支持这样的写法的:

但是不支持这样的写法:

3.3const
迭代器的实现与模板参数的巧用
前面我们已经实现了,可读可写的迭代器,现在我们就来实现一下只读迭代器:
const iterator
,
其实从功能上看,这个迭代器与原来的迭代器十分的相似,只是不能对指向对象的值进行修改,因此我们就只需要对* 和 ->
运算符重载的时候稍加修改就可以得到我们想要的结果,即再创建一个类:

其他的共同功能粗需要改动,只需要改动下面两个重载函数:
const T& operator*()
{
return _nd->_data;
}
const T* operator->()
{
return &_nd->_data;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
紧接着我们还需要在llist
类中实现const对象
专用的begin()、end()


接下来我们来验证一下效果:

现在consr iterator
也实现好了,但是这里依然还存在问题,这样我们将相当于为迭代器实现了两个类,这两个类的攻击能还高度重合,这并没有,体现出模板函数的简洁性,因此我们还可以通过其他的方式来优化我们现在的代码。
通过对源码的分析,参照我们或许可以从中得到一些启发, 从源码中可知我们可以得知,通过对模板参数的巧用就可以的实现代码的简化:

接下来我们就只需要将代码稍加改动就可以实现我们的目的:


接下来,我们再次运行看看是否可以达到简化代码的效果:

可以发现现在的代码依然有效,代码简化成功!!
四、丰富构造函数、list增删查改的实现
现在我们已经完成了list
的简单构造函数,现在我们就可以参照 c++ library
完成其他的构造函数:
4.1 list(size_t n, const T& x = T())
我们要实现的这个函数和标准库中的一样,并且直接复用我峨嵋你已经实现的函数就好了:
list(size_t n, const T& x = T())
{
for (int i = 0; i < n; i++)
{
push_back(x);
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
直接来演示一下的效果:

4.2 拷贝构造
我们就只需要将被拷贝链表的元素一个一个的拷贝进链表就行了:
list(const list<T>& l1)
{
empty_init();
for (auto& e : l1) push_back(e);
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
我们来测试一下效果:

4.3 插入函数的实现(insert)
想要在这个双向链表中插入节点,我们就需要
所以,insert函数定义为: void insert(iterator pos, const T& val = T())
iterator insert(iterator pos, const T& val = T())
{
node* cur = pos._nd;
node* prev = pos._nd->_prev;
node* insrt = new node(val);
prev->_next = insrt;
insrt->_prev = prev;
insrt->_next = cur;
cur->_prev = insrt;
return iterator(cur->_prev);
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
这个函数也十分的简单如果,有的uu还没有接触过链表,或者是已经忘了链表的增删查改,可以移步去看看我之前的博客:链表的介绍
4.4链表的删除(earse)
关于erase和函数的定义,我们就只需要拿到需要删除的位置就可以了, 所以定义为:void erase(iterator pos)
iterator erase(iterator pos)
{
assert(pos != end());
node* cur = pos._nd;
node* prev = cur->_prev;
prev->_next = cur->_next;
cur->_next->_prev = prev;
delete cur;
return iterator(prev->_next);
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
现在我们就直接来测试一下这个函数是否可以满足我们的要求:

由此可知我们实现的都没有问题。
4.5链表元素的查找(find)
定义find函数时,我们就只需要给函数一个特定的需要查找到的值,然后使这个函数返回这个元素的位置(迭代器)
iterator find(const T& val)
{
auto it = begin();
while (it != end() && *it != val)
{
it++;
}
if (*it == val) return it;
return nullptr;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

4.6 头删和尾删操作
前面我们已经实现了earse
现在我们就只需要对这个和拿书尽心你给复用就好了:
void pop_front()
{
erase(++end());
}
void pop_back()
{
erase(--end());
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
接下来我们就直接来演示这个函数是否有用:

五、析构函数与clear函数
最后我们就来实现一下list
的析构函数,我们还是继续函数的复用:
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
auto it = begin();
while (it != end()) it = erase(it);
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
现在我们就将已有的链表清空试试:

六、代码展示
list.h
#pragma once
#include
#include
using namespace std;
namespace zdl
{
template <class T>
struct ListNode
{
T _data;
ListNode<T>* _next;
ListNode<T>* _prev;
ListNode(const T& val = T())
:_data(val)
,_next(nullptr)
,_prev(nullptr)
{}
};
template<class T, class Ref, class Ptr>
struct list_iterator
{
typedef ListNode<T> node;
typedef list_iterator<T, Ref, Ptr> self;
node* _nd;
list_iterator(node* nd)
:_nd(nd)
{}
Ref operator*()
{
return _nd->_data;
}
Ptr operator->()
{
return &_nd->_data;
}
self& operator++()
{
_nd = _nd->_next;
return *this;
}
self& operator--()
{
_nd = _nd->_prev;
return *this;
}
self& operator+(size_t n)
{
while (n--)
{
_nd = _nd->_next;
}
return *this;
}
self& operator-(size_t n)
{
while (n--)
{
_nd = _nd->_prev;
}
return *this;
}
self operator++(int)
{
self tmp = _nd;
_nd = _nd->_next;
return tmp;
}
self operator--(int)
{
self tmp = _nd;
_nd = _nd->_prev;
return tmp;
}
bool operator!=(const self& s)
{
return _nd != s._nd;
}
};
template<class T>
class list
{
public:
typedef ListNode<T> node;
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
void empty_init()
{
_head = new node();
_head->_next = _head;
_head->_prev = _head;
}
list()
{
empty_init();
}
list(const list<T>& l1)
{
empty_init();
for (auto& e : l1) push_back(e);
}
list(size_t n, const T& x = T())
{
empty_init();
for (size_t i = 0; i < n; i++)
{
push_back(x);
}
}
void push_back(const T& val)
{
node* creat = new node(val);
node* tail = _head->_prev;
tail->_next = creat;
creat->_prev = tail;
creat->_next = _head;
_head->_prev = creat;
}
void push_front(const T& val)
{
node* fnode = new node(val);
fnode->_next = _head->_next;
fnode->_prev = _head;
_head->_next->_prev = fnode;
_head->_next = fnode;
}
void Print_list()
{
node* cur = _head->_next;
while (cur != _head)
{
cout << cur->_data << " ";
cur = cur->_next;
}
cout << endl;
}
iterator insert(iterator pos, const T& val = T())
{
node* cur = pos._nd;
node* prev = pos._nd->_prev;
node* insrt = new node(val);
prev->_next = insrt;
insrt->_prev = prev;
insrt->_next = cur;
cur->_prev = insrt;
return iterator(cur->_prev);
}
iterator erase(iterator pos)
{
assert(pos != end());
node* cur = pos._nd;
node* prev = cur->_prev;
prev->_next = cur->_next;
cur->_next->_prev = prev;
delete cur;
return iterator(prev->_next);
}
iterator find(const T& val)
{
auto it = begin();
while (it != end() && *it != val)
{
it++;
}
if (*it == val) return it;
return nullptr;
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
auto it = begin();
while (it != end()) it = erase(it);
}
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
private:
node* _head;
};
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 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
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
test.cpp
#include"list.h"
#include
struct pos
{
int row;
int col;
pos(const int& x = 0, const int& y = 0)
:row(x)
,col(y)
{}
void pos_show()
{
printf("(%d, %d) ",row, col);
}
};
int main()
{
zdl::list<int> l1(10, 10);
cout <<"l1:" << endl;
l1.Print_list();
zdl::list<int> l2(l1);
cout << "l2:" << endl;
l2.Print_list();
return 0;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 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
好,常用的接口我们就实现了,list学习到此告一段落,再见!!
data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/2302_79964175/article/details/145228019","extend1":"pc","ab":"new"}">>
评论记录:
回复评论: