- 【本节目标】
- 1.list的介绍和使用
- 2.list的深度剖析和模拟实现
- 3.list和vector的对比
目录
1.list的介绍及使用
1.1list介绍
- 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- 2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素。
- 3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
- 4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
- 5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list 的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)
1.2list使用
list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展 的能力。以下为list中一些常见的重要接口。
1.2.1list的构造
构造函数 | 接口说明 |
list (size_type n, const value_type& val = value_type()) | 构造的list中包含n个值为val的元素 |
list() | 构造空的list |
list (const list& x) | 拷贝构造函数 |
list (InputIterator first, InputIterator last) | 用[first, last)区间中的元素构造list |
void TestList1()
{
list<int> l1; // 构造空的l1
list<int> l2(4, 100); // l2中放4个值为100的元素
list<int> l3(l2.begin(), l2.end()); // 用l2的[begin(), end())左闭右开的区间构造l3
list<int> l4(l3); // 用l3拷贝构造l4
// 以数组为迭代器区间构造l5
int array[] = { 16,2,77,29 };
list<int> l5(array, array + sizeof(array) / sizeof(int));
// 列表格式初始化C++11
list<int> l6{ 1,2,3,4,5 };
// 用迭代器方式打印l5中的元素
list<int>::iterator it = l5.begin();
while (it != l5.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// C++11范围for的方式遍历
for (auto& e : l5)
cout << e << " ";
cout << endl;
}
1.2.1list_iterator的使用
此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。
list 的迭代器是将原生指针进行封装后的自定义类型,因为与 vector 和 string 等在物理结上是连续的,所以可以直接将原生指针作为迭代器,而 list 不行,因为 list 的存储方式是链式结构,是分散的,所以不能直接对原生指针进行++等操作,所以得对 list 的指针进行封装,下面模拟实现的时候会具体讲。
函数声明 | 接口说明 |
begin+end | 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器 |
rbegin+rend | 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置 |
【注意】
- begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动 2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动 。
- rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动。
void TestList2()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array + sizeof(array) / sizeof(array[0]));
// 使用正向迭代器正向list中的元素
// list<int>::iterator it = l.begin(); // C++98中语法
auto it = l.begin(); // C++11之后推荐写法
while (it != l.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// 使用反向迭代器逆向打印list中的元素
// list<int>::reverse_iterator rit = l.rbegin();
auto rit = l.rbegin();
while (rit != l.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
1.2.3list_capacity
函数说明 | 接口说明 |
empty | 检测list是否为空,是返回true,否则返回false |
size | 返回list中有效节点的个数 |
1.2.4list element access
函数说明 | 接口说明 |
front | 返回list的第一个节点中值的引用 |
back | 返回list的最后一个节点中值的引用 |
1.2.5 list modifiers
函数说明 | 接口说明 |
push_front | 在list首元素前插入值为val的元素 |
pop_fornt | 删除list中第一个元素 |
push_back | 在list尾部插入值为val的元素 |
pop_back | 删除list中最后一个元素 |
insert | 在list position 位置中插入值为val的元素 |
erase | 删除list position位置的元素 |
swap | 交换两个list中的元素 |
clear | 清空list中的有效元素 |
void TestList3()
{
int array[] = { 1, 2, 3 };
list<int> L(array, array + sizeof(array) / sizeof(array[0]));
// 在list的尾部插入4,头部插入0
L.push_back(4);
L.push_front(0);
PrintList(L);
// 删除list尾部节点和头部节点
L.pop_back();
L.pop_front();
PrintList(L);
int array1[] = { 1, 2, 3 };
list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));
// 获取链表中第二个节点
auto pos = ++L.begin();
cout << *pos << endl;
// 在pos前插入值为4的元素
L.insert(pos, 4);
PrintList(L);
// 在pos前插入5个值为5的元素
L.insert(pos, 5, 5);
PrintList(L);
// 在pos前插入[v.begin(), v.end)区间中的元素
vector<int> v{ 7, 8, 9 };
L.insert(pos, v.begin(), v.end());
PrintList(L);
// 删除pos位置上的元素
L.erase(pos);
PrintList(L);
// 删除list中[begin, end)区间中的元素,即删除list中的所有元素
L.erase(L.begin(), L.end());
PrintList(L);
}
1.2.6 list Operations
函数说明 | 接口说明 |
splice | 将 list1 中的元素转移到 list2中 (不是拷贝,而是搬移) |
remove | 将 list 中所有 val 节点删掉 |
unique | 去掉 list 中的重复的元素 (需要先排序,配合 list 类自己的sort函数一起使用) |
merge | 在相同类型且分别有序的情况下,将 list2 中的元素都搬移到 list1 中,且按大小进行排序 (list2变成空的) |
sort | 排序 list 中的元素(效率低,不推荐) |
reverse | 逆置 list 中的元素 |
为什么 list 类中要重新实现sort函数,而不是使用算法库里面的?
理论上来说,算法库中实现的函数模板是通用的,但是因为算法库里面的一些函数是需要接受不同类型的迭代器的,而 list 类的迭代器属于双向迭代器,算法库里面sort函数底层使用的是快速排序,而快速排序要求容器迭代器接收的是随机迭代器,迭代器类型不符合,所以使用不了库里面的sort函数以及其他一些函数。
1.2.7 list迭代器失效问题
前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
void TestListIterator1()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array+sizeof(array)/sizeof(array[0]));
auto it = l.begin();
while (it != l.end())
{
// erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值
l.erase(it);
++it;
}
}
// 改正
void TestListIterator()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array+sizeof(array)/sizeof(array[0]));
auto it = l.begin();
while (it != l.end())
{
l.erase(it++); // it = l.erase(it);
}
}
2.list模拟实现
2.1模拟实现list
1.构造结点
为了做到通用,显然在 C++ 中就得借助 模板 来实现通用!
namespace liren
{
template <class T>
struct list_node //在这里我们将其设置为结构体struct,也可以设置为类,但是要将成员变量设为public
{
list_node<T>* _next;
list_node<T>* _prev;
T _val;
};
}
接下来我们对节点附上构造函数,因为 C++ 中,每次开辟一个对象就会调用其构造函数进行初始化,这就很方便!
- 将数据给到val
- 将_next和_prev置为空
- 这些任务我们可以写到 struct ListNode 的构造函数中,我们还可以设计成全缺省,给一个匿名对象 T() 。如此一来,如果没有指定初识值,它就会按模板类型去给对应的初始值了。
namespace liren
{
template <class T>
struct list_node
{
list_node(const T& val = T())
:_val(val),
_next(nullptr),
_prev(nullptr)
{}
list_node<T>* _next;
list_node<T>* _prev;
T _val;
};
}
2.构造框架
- 考虑到我们写的节点 list_node<T> 名字比较长,所以我们用 typedef 将其别名设为 Node。
- 又考虑到可能后面的拷贝构造以及赋值重载也需要构造头节点,所以我们将构造头节点独立为一个函数 CreateHead()。
- 构建的是哨兵位头结点,我们需要先设计出_head,再将_prev,_next指向默认的位置
初始化头结点
//初始化头结点
void CreateHead()
{
_head = new Node;
_head->_next = _head; //让前后指针都指向头节点本身
_head->_prev = _head;
}
namespace liren
{
template <class T>
class list
{
public:
typedef list_node<T> Node; //重命名为Node
public:
/* 构造函数:初始化头结点 */
list()
{
CreateHead(); //调用函数去构造头节点
}
private:
Node* _head; // 头结点指针
};
}
3.迭代器的实现
list 和之前的 vector/string 不同,list 是链式结构,在物理空间上是不连续的,所以直接对 list 的原生指针进行操作,比如++操作,是没办法移到下一个 list 元素的位置的(除非运气非常好,刚刚好下个节点位置就在上一个节点位置的下个地址处)。
所以我们要对list的迭代器进行封装,并对运算符进行重载,这样就实现了迭代器。
首先,因为 operator++ 以及 operator-- 等函数操作都是返回 list_iterator 类型的,所以可以先将迭代器重命名为 Self,减少长度,以及后面我们需要传多个参数的模板时候,这个重命名就可以方便了很多事情,不需要我们去修改很多地方!
构造迭代器
template <class T>
struct list_iterator
{
typedef list_node<T> Node; //将节点重命名为Node
typedef list_iterator<T> Self; //将迭代器重命名为Self
/* 迭代器的构造 */
list_iterator(Node* node = nullptr) //设置一个nullptr的缺省值
:_node(node)
{}
Node* _node;
};
迭代器的拷贝构造、赋值重载和析构函数
- 我们使用迭代器去访问和修改 list 或者其他容器的元素,本质就是要得到该处元素的指针,那么我们就没必要去做深拷贝,直接做浅拷贝,将该处的地址浅拷贝给迭代器构造即可。即当前迭代器赋值给另一个迭代器是不需要深拷贝的,浅拷贝就可以。
- 迭代器里面虽然有一个节点的指针,但是该节点并不是由迭代器进行管理的,而是由 list 进行管理的,list 类的生命周期结束后会调用析构函数对节点进行释放的,而迭代器只是负责对该节点进行一些操作,并不需要对节点进行释放,也不允许对节点进行释放。
4.赋值运算符重载函数
1.operator++和++operator
- 前置++返回的是本体。
- 后置++返回的不是本体,是一个临时的迭代器对象,不能用传引用返回。
前置++
/* ++it */
Self& operator++()
{
_node = _node->_next; // 让 _node指向下一个结点
return *this; // 返回++后的值
}
后置++
/* it++ */
Self operator++(int)
{
Self tmp(*this); //拷贝构造一个tmp存储原来的值
_node = _node->_next;
return tmp; //返回一个临时对象
}
2.operator*和operator->
- operator* 的功能是 解引用,也就是获得该处的数据。
- operator-> 的功能是 返回该处数据的地址。
operator*
T& operator*()
{
return _node->_val; //返回结点的数据
}
operator->
T* operator->()
{
return &(_node->val); //返回_node->val的地址
}
3.operator+和operator-
因为list是链式结构,所以operator+是指向下一个结点的位置,operator是指向上一个结点的位置 这与++和--是一样的只不过区分前置后置
operator--
/* --it */
Self& operator--()
{
_node = _node->prev;
return *this;
}
/* it-- */
Self operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
4.operator!=和operator==
bool operator!=(const Self& s) //使用传引用提高效率
{
return _node != s._node;
}
bool operator==(const Self& s)
{
return _node == s._node;
}
5.构造const迭代器
与普通对象的迭代器不同,const 对象迭代器只能读不能写,而通常迭代器进行修改和读取都是通过 operator*和operator-> 来实现,所以我们只需要实现这两个函数的 const 版本即可。
template<class T>
class list {
typedef list_node<T> Node; // 重命名为Node
public:
/* 迭代器 */
typedef list_iterator<T> iterator;
typedef const_list_iterator<T> const_iterator; // 重命名const迭代器
// 👇 const 迭代器的 begin 和 end
const_iterator begin() const {
return const_iterator(_pHead->_next); // 调用 const_list_iterator<T>
}
const_iterator end() const {
return const_iterator(_pHead);
}
iterator begin() {
return iterator(_pHead->_next); // 调用 list_iterator<T>
}
iterator end() {
return iterator(_pHead);
}
...
}
const_list_iterator(Node* x)
: _node(x)
{}
const T& operator*() const
{
return _node->_val; // 返回结点的数据
}
const T* operator->() const
{
return &(_node->val);
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self operator++(int)
{
_const_list_iterator<T> tmp(*this);
_node = _node->_next;
return tmp;
}
//....只展示部分函数,其他都是一样的
}
这样看起来非常的冗余,让我们来看看原码
这个用法的意思就是在定义 template 模板时候增加两个参数:一个是 Ref(reference引用的意思),一个是 Ptr(pointer)。
这样的话,我们 operator 的返回值* 我们不要用 T&了,我们改成 Ref;而 operator-> 的返回值我们改成 Ptr。
template <class T, class Ref, class Ptr>
struct list_iterator
{
typedef _list_node<T> Node; //将节点重命名为Node
typedef list_iterator<T, Ref, Ptr> Self; //将迭代器重命名为Self
/* 迭代器的构造 */
list_iterator(Node* node = nullptr) //设置一个nullptr的缺省值
:_node(node)
{}
Ref operator*()
{
return _node->_val; //返回结点的数据
}
Ptr operator->()
{
return &(_node->val); //返回_node->val的地址
}
//......其他函数还是一样不变,前提是将迭代器重命名为了Self
}
6.反向迭代器
通过前面例子知道,反向迭代器的++就是正向迭代器的--,反向迭代器的--就是正向迭代器的++,因此反向迭 代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。
namespace liren
{
//反向迭代器的自主实现版本
template <class iterator, class Ref, class Ptr>
struct reverse_iterator
{
// Iterator是哪个容器的迭代器,reverse_iterator<Iterator>就可以适配出哪个容器的反向迭代器。复用的体现
typedef reverse_iterator<iterator, Ref, Ptr> Self; //将反向迭代器重命名为Self
reverse_iterator(iterator it)
:_it(it)
{}
Ref operator*()
{
iterator prev(_it);
return *(--prev); // 注意结构上的错位
}
Ptr operator->()
{
iterator prev = _it;
return &(operator*()); //复用上面的operator*()
}
Self& operator++() //与正向迭代器相反,反向迭代器的++其实是正向迭代器的--,反之也成立
{
--_it;
return *this;
}
Self operator++(int)
{
Self tmp = _it;
--_it;
return tmp;
}
Self& operator--()
{
++_it;
return *this;
}
Self operator--(int)
{
Self tmp = _it;
++_it;
return tmp;
}
bool operator!=(const Self& s) const
{
return _it != s._it;
}
iterator _it;
};
template <class T>
class list
{
public:
typedef list_node<T> Node;
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
//记得这里第一个参数是要传iterator,而不是T,因为我们是通过iterator来包装反向迭代器的
typedef reverse_iterator<iterator, T&, T*> re_iterator;
typedef reverse_iterator<iterator, const T&, const T*> const_re_iterator;
public:
re_iterator rbegin()
{
return re_iterator(end());
}
const_re_iterator rbegin() const
{
return const_re_iterator(end());
}
re_iterator rend()
{
return re_iterator(begin());
}
const_re_iterator rend() const
{
return const_re_iterator(begin());
}
…………
private:
Node* _head;
};
}
7.insert函数
在 pos 位置前插入,我们通过 pos 去找到前驱 prev,之后创建新结点,再进行 “缝合” 操作,比较简单。
// 在pos位置前插入值为val的节点
void insert(iterator pos, const T& x)
{
Node* newnode = new Node(x); //开辟新节点
Node* prev = pos._node;
newnode->_prev = prev->_prev;
newnode->_next = prev;
newnode->_prev->_next = newnode;
prev->_prev = newnode;
}
有了insert函数,我们就可以赋用insert函数来实现push_back函数
//尾插
void push_back(const T& x)
{
//第一种自己实现
/*Node* newnode = new Node(x);
_head->_prev->_next = newnode;
newnode->_prev = _head->_prev;
newnode->_next = _head;
_head->_prev = newnode;*/
//第二种复用insert
insert(end(), x); //在end(pHead)前插入,即尾插
}
//头插
void push_front(const T& x)
{
insert(begin(), x); // 在begin(头结点的下一个结点)前插入,即头插
}
8.erase函数
删除 pos 位置结点,步骤如下:
① 找到 pos 的前驱和后继
② 释放 pos 位置结点
③ 将已删除的 pos 结点的前驱和后继 “缝合”
// 删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos)
{
assert(pos != end()); // 防止头结点被删除
Node* cur = pos._node; // 找到pos位置的结点
Node* cur_prev = cur->_prev; // 找到pos的前驱
Node* cur_next = cur->_next; // 找到pos的后继
// 删除cur
delete cur;
// 缝合: cur_prev <-> cur(删) <-> cur_next
cur_prev->_next = cur_next;
cur_next->_prev = cur_prev;
return iterator(cur_next);
}
由于 erase 函数删掉了一个节点后,这个迭代器肯定失效了!为了让迭代器不失去作用,我们 erase 函数返回下一个节点的迭代器。
实现了erase函数,我们就能实现头删和尾删了
/* 尾删 */
void pop_back()
{
erase(--end()); // 删除最后一个元素,即尾结点
}
/* 头删 */
void pop_front()
{
erase(begin()); // 删除头结点的下一个结点(即begin位置的结点)
}
9.访问操作函数
// List的元素访问操作
// 注意:List不支持operator[]
T& front()
{
return _head->_next->_val;
}
const T& front()const
{
return _head->_next->_val;
}
T& back()
{
return _head->_prev->_val;
}
const T& back()const
{
return _head->_prev->_val;
}
10.容量相关的函数
//size函数
size_t size() const
{
Node* cur = _head->_next;
size_t count = 0;
while (cur != _head)
{
count++;
cur = cur->_next;
}
return count;
}
//empty函数
bool empty() const
{
return _head == _head->_next;
}
//resize函数
void resize(size_t n, const T& val = T())
{
size_t sz = size();
if (n <= sz) //小于list长度
{
while (n <= sz)
{
pop_back();
sz--;
}
}
else //大于list长度
{
while (sz < n)
{
push_back(x);
sz++;
}
}
}
11.list的拷贝构造,赋值重载,析构函数
1.析构函数
我们需要把节点释放掉,防止内存泄漏
这里我们先讲一下clear函数
我们后面在实现析构和拷贝构造的时候,可能需要用到清理 list 的函数,所以我们就做了 clear 函数“负责清理有效元素个数"。
我们可以自己实现,但是没必要,因为可以用 erase 进行复用,但是注意,要传一个 iterator 给erase函数而不是传节点!
void clear() { iterator cur = begin(); //注意要传迭代器,因为等会erase只接收迭代器 while (cur != end()) { erase(cur++); //因为erase完迭代器会失效,所以要重新赋值 } }
析构函数代码如下
~list()
{
clear(); //先将有效元素释放掉
//然后将头节点也释放掉,并置空
delete _head;
_head = nullptr;
}
这样子我们就把析构函数写完啦!
但是这里就出现了问题,那就是拷贝构造或者赋值的浅拷贝问题引发的析构问题:浅拷贝只是将指针拷贝过去,两个 list 指向的是同一块空间,最后释放的时候,同一块空间被析构了两次,这样子程序就报错了!
所以我们必须自己实现拷贝构造以及赋值重载!
2.拷贝构造
与 vector、string 类似,list 的拷贝构造也有传统写法与现代写法之分!
同样的,深拷贝就是重新开辟一个空间,然后将原空间的数据复制过来。
传统写法
//传统写法的拷贝构造
list(const list<T>& l)
{
//创建新的头节点(该函数在上面实现了)
CreateHead();
for (auto e : l)
{
push_back(e);
}
}
现代写法
//拷贝构造的现代写法
template <class Iterator>
list(Iterator first, Iterator last)
{
CreateHead();
while (first != last)
{
push_back(*first);
++first;
}
}
list(const list<T>& l)
{
CreateHead();
// 用l中的元素构造临时的temp,然后与当前对象交换
list<T> temp(l.begin(), l.end());
this->swap(temp);
}
3.赋值重载
传统写法
list<T>& operator=(list<T> L)
{
if (this != &L) //判断一下是否为自己给自己赋值,若是的话则没必要赋值
{
clear();
for (auto e : L)
{
push_back(e);
}
}
return *this;
}
现代写法
list<T>& operator=(list<T> l) //接收的不是引用,而只是一个值
{
this->swap(l);
return *this;
}
3.完整代码
list.h
namespace bit
{
template<class T>
struct list_node
{
T _data;
list_node<T>* _next;
list_node<T>* _prev;
list_node(const T& x = T())
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{}
};
// T T& T*
// T cosnt T& const T*
template<class T, class Ref, class Ptr>
struct __list_iterator
{
typedef list_node<T> Node;
typedef __list_iterator<T, Ref, Ptr> self;
Node* _node;
__list_iterator(Node* node)
:_node(node)
{}
self& operator++()
{
_node = _node->_next;
return *this;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
self operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
self operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const self& s)
{
return _node != s._node;
}
bool operator==(const self& s)
{
return _node == s._node;
}
};
//template<class T>
//struct __list_const_iterator
//{
// typedef list_node<T> Node;
// typedef __list_const_iterator<T> self;
// Node* _node;
// __list_const_iterator(Node* node)
// :_node(node)
// {}
// self& operator++()
// {
// _node = _node->_next;
// return *this;
// }
// self& operator--()
// {
// _node = _node->_prev;
// return *this;
// }
// self operator++(int)
// {
// self tmp(*this);
// _node = _node->_next;
// return tmp;
// }
// self operator--(int)
// {
// self tmp(*this);
// _node = _node->_prev;
// return tmp;
// }
// // *it = 10
// const T& operator*()
// {
// return _node->_data;
// }
// // it->a1 = 10
// const T* operator->()
// {
// return &_node->_data;
// }
// bool operator!=(const self& s)
// {
// return _node != s._node;
// }
// bool operator==(const self& s)
// {
// return _node == s._node;
// }
//};
template<class T>
class list
{
typedef list_node<T> Node;
public:
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
//typedef __list_const_iterator<T> const_iterator;
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
iterator begin()
{
//return iterator(_head->_next);
return _head->_next;
}
iterator end()
{
//return iterator(_head->_next);
return _head;
}
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
empty_init();
}
// lt2(lt1)
list(const list<T>& lt)
{
empty_init();
for (auto e : lt)
{
push_back(e);
}
}
// lt3 = lt1
/*list<int>& operator=(const list<int>& lt)
{
if (this != <)
{
clear();
for (auto e : lt)
{
push_back(e);
}
}
return *this;
}*/
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
// lt3 = lt1
list<int>& operator=(list<int> lt)
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
void push_back(const T& x)
{
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
iterator insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* newnode = new Node(x);
Node* prev = cur->_prev;
// prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
return iterator(newnode);
}
iterator erase(iterator pos)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
delete cur;
prev->_next = next;
next->_prev = prev;
--_size;
return iterator(next);
}
size_t size()
{
return _size;
}
private:
Node* _head;
size_t _size;
};
void test_list1()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
// 封装屏蔽底层差异和实现细节
// 提供统一的访问修改遍历方式
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
*it += 20;
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
/*set<int> s;
s.insert(1);
s.insert(3);
s.insert(2);
s.insert(4);
set<int>::iterator sit = s.begin();
while (sit != s.end())
{
cout << *sit << " ";
++sit;
}
cout << endl;*/
}
void test_list2()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
list<int> lt1(lt);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
list<int> lt2;
lt2.push_back(10);
lt2.push_back(200);
lt2.push_back(30);
lt2.push_back(40);
lt2.push_back(50);
lt1 = lt2;
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
for (auto e : lt2)
{
cout << e << " ";
}
cout << endl;
}
struct AA
{
AA(int a1 = 0, int a2 = 0)
:_a1(a1)
,_a2(a2)
{}
int _a1;
int _a2;
};
void test_list3()
{
list<AA> lt;
lt.push_back(AA(1, 1));
lt.push_back(AA(2, 2));
lt.push_back(AA(3, 3));
list<AA>::iterator it = lt.begin();
while (it != lt.end())
{
//cout << (*it)._a1<<" "<<(*it)._a2 << endl;
cout << it->_a1 << " " << it->_a2 << endl;
cout << it.operator->()->_a1 << " " << it.operator->()->_a1 << endl;
++it;
}
cout << endl;
int* p = new int;
*p = 1;
AA* ptr = new AA;
ptr->_a1 = 1;
}
//void print_list(const list<int>& lt)
//{
// list<int>::const_iterator it = lt.begin();
// while (it != lt.end())
// {
// //*it = 10;
// cout << *it << " ";
// ++it;
// }
// cout << endl;
// for (auto e : lt)
// {
// cout << e << " ";
// }
// cout << endl;
//}
//void print_list(const list<int>& lt)
//{
// list<int>::const_iterator it = lt.begin();
// while (it != lt.end())
// {
// //*it = 10;
// cout << *it << " ";
// ++it;
// }
// cout << endl;
///* for (auto e : lt)
// {
// cout << e << " ";
// }
// cout << endl;*/
//}
// 实例化
//template<typename T>
template<class T>
//void print_list(const list<T>& lt)
//{
// // list<T>未实例化的类模板,编译器不能去他里面去找
// // 编译器就无法list<T>::const_iterator是内嵌类型,还是静态成员变量
// // 前面加一个typename就是告诉编译器,这里是一个类型,等list<T>实例化
// // 再去类里面去取
// typename list<T>::const_iterator it = lt.begin();
// while (it != lt.end())
// {
// cout << *it << " ";
// ++it;
// }
// cout << endl;
//}
// 10:20继续
// 模板(泛型编程)本质,本来应该由我们干的活交给编译器
template<typename Container>
void print_container(const Container& con)
{
typename Container::const_iterator it = con.begin();
while (it != con.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
void test_list4()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
//print_list(lt);
print_container(lt);
list<string> lt1;
lt1.push_back("1111111111111");
lt1.push_back("1111111111111");
lt1.push_back("1111111111111");
lt1.push_back("1111111111111");
lt1.push_back("1111111111111");
//print_list(lt1);
print_container(lt1);
vector<string> v;
v.push_back("222222222222222222222");
v.push_back("222222222222222222222");
v.push_back("222222222222222222222");
v.push_back("222222222222222222222");
//print_list(v);
print_container(v);
}
}
test.cpp
int main()
{
//test_list5();
//test_op();
bit::test_list4();
return 0;
}