努力的最大好处,就在于你可以选择你想要的生活,而不是被迫随遇而安。 
🎓list介绍
1、 list是可以在**常数范围O(1)**内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代,但list容器不适合用来做排序。 2、list的底层是一个循环双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
🎓构造函数
在C++98中list的构造函数加上拷贝构造函数为如下四个。下面我们就模拟实现这里的全部的构造函数。当然我们这里并没有像标准库一样使用空间配置器来创建空间,使用的是new 运算符,但原理都是创建一块新的空间。 
🐱?💻无参构造函数
这里我们模拟默认的构造函数,默认值为T(),其含义为:该类型的默认值int类型就为0,char类型为’\0’,自定义类型会调用其构造函数来初始化这个匿名对象得到默认值。
list()
{
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
}
🍖有参构造函数
list(size_t size, const T& val=T())
{
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
while (size--)
{
this->push_back(val);
}
}
注意:
- 1、该构造函数知道其需要用于存储n个数据的空间,所以我们使用new运算符开辟好空间,后用push_back()函数来尾插入val值。
- 2、该构造函数还需要实现两个重载函数,如果不实现其重载函数,会导致本来想调用n个元素构造函数时,编译器会调用到我们下面要模拟实现的模板区间构造函数。
list(long size, const T& val = T())
{
assert(size > 0);
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
while (size--)
{
this->push_back(val);
}
}
list(int size, const T& val = T())
{
assert(size > 0);
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
while (size--)
{
this->push_back(val);
}
}
可以看到,这两个重载函数与之不同的就是其参数size的类型不同,但这却是必要的,否则当我们使用以下代码时,编译器会优先与模板区间构造函数相匹配。
ZJ::list<int>L(2,1);
并且因为构造函数2当中对参数first和last进行了解引用(*)(而int类型不能进行解引用操作)而报错。
🚀模板区间构造函数
最后就是我们上面一直说到的模板区间构造函数了,因为该迭代器区间可以是其他容器的迭代器区间,该函数接收到的迭代器的类型是不确定的,所以我们这里需要将该构造函数设计为一个函数模板,在函数体内将该迭代器区间的数据一个个尾插到容器当中即可。
template <class InputIterator>
list(InputIterator first, InputIterator last)
{
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
while (first != last)
{
node* newnode = new node(*first);
node* tail = this->m_head->m_prev;
tail->m_next = newnode;
newnode->m_prev = tail;
newnode->m_next = this->m_head;
this->m_head->m_prev = newnode;
++first;
}
}
注意:
- 1、该区间必须是前闭后开[obj.begin(),obj.end());
🎓拷贝构造函数
拷贝构造的思想是我们是很容易想到的,就是遍历一遍我们要拷贝的对象obj链表,并将obj每个元素的值给this对象的每一个对应的结点,并将其每个结点都链接起来。
list(const list<T>& obj)
{
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
const_iterator it = obj.begin();
while (it != obj.end())
{
node* newnode = new node(it.m_pnode->m_val);
node* tail = this->m_head->m_prev;
tail->m_next = newnode;
newnode->m_prev = tail;
newnode->m_next = this->m_head;
this->m_head->m_prev = newnode;
++it;
}
}
🎓赋值运算符重载
根据我们之前的博客的往历,这里我们采用现代写法,及用obj调用拷贝构造函数创建一个临时对象temp,并将temp与我们的this指针指向的对象的指针交换即可。
list<T>& operator=(const list<T>& obj)
{
if (this != &obj)
{
list<T>temp(obj);
this->swap(temp);
}
return *this;
}
注意:
- 1、上面的temp临时对象出了
if 语句时就会自动调用析构函数进行释放内存,故不会造成内存的泄漏等问题。
🎓析构函数
析构函数这里,我就有点偷懒啦,复用了我们下面要模拟实现的pop_front()函数,大致思路就是从头到尾一个一个删除结点,并把头结点也删除并至空。
~list()
{
iterator it = this->begin();
while (it != this->end())
{
++it;
this->pop_front();
}
delete this->m_head;
this->m_head = nullptr;
}
🎓迭代器
在C++中我们的迭代器有如下几种:  下面我只模拟begin()和end()迭代器。
iterator begin()
{
return iterator(this->m_head->m_next);
}
const_iterator begin() const
{
return const_iterator(this->m_head->m_next);
}
iterator end()
{
return iterator(this->m_head);
}
const_iterator end() const
{
return const_iterator(this->m_head);
}
上面我们模拟实现了我们的迭代器,并且有普通迭代器和const迭代器。但是我们还要了解迭代器的原理是上面,在之前我们的博客中我们说过并不是每个迭代器都是原生指针,其中我们的list就是封装了一个指针变量来达到实现iterator的结果。
typedef list_iterator<T,T&,T*> iterator;
typedef list_iterator<T,const T&,const T*> const_iterator;
可能有人会疑惑,上面这两个迭代器不都差不多嘛,只是名字不一样,为什么不直接在类型上加const ,而是在模板参数上指定加上const 属性呢?我们要知道由于const对象只能调用常函数,但是平时我们使用std::list时是不是可以支持 ++、-- 呢?如果是const对象,它只能调用常函数,一旦加上变成const函数,那我们的const迭代器就不能进行++、–、’ * '等,而我们要达到的效果是可以进行++、–等,但仅仅是不能其元素的值而已。所以 我们这里封装了一个模板指针。我们通过模板的参数不同来控制它的读取操作。
🗼迭代器构造函数
就是将一个指针传过来,并赋给我们的成员变量m_pnode。
list_iterator(node* pnode)
:m_pnode(pnode)
{}
🏝迭代器关系运算符重载
因为我们要实现迭代器的相关遍历操作例如下面的代码: ZJ::list<int>::iterator it = L1.begin(); it != L1.end()
bool operator!=(const myself&obj) const
{
return this->m_pnode != obj.m_pnode;
}
bool operator==(const myself& obj) const
{
return this->m_pnode == obj.m_pnode;
}
🧸迭代器++ --运算符重载
其中下面返回的myself 类型其实就是我们的迭代器这个类的类型,只是我们将其typedef了一下代码如下:
typedef list_iterator<T, Ref, Ptr>myself;
这里重载的前置与后置要分别开来,后置需要使用int占位符来占位,不然会发生错误。
myself& operator++()
{
this->m_pnode = this->m_pnode->m_next;
return *this;
}
myself operator++(int)
{
const myself temp(*this);
this->m_pnode = this->m_pnode->m_next;
return temp;
}
myself& operator--()
{
this->m_pnode = this->m_pnode->m_prev;
return *this;
}
myself operator--(int)
{
const myself temp(*this);
this->m_pnode = this->m_pnode->m_prev;
return temp;
}
🗽迭代器 * 运算符重载
由于我们知道Ref是由两种类型的,一种是T&,另一种是const T&,所以当我们的对象是const对象时,我们可以控制它不让其修改。
Ref operator* ()
{
return this->m_pnode->m_val;
}
🪐迭代器 -> 运算符重载
为什么要重载->运算符呢?这是因为如果我们list中存储的是自定义类型时,我们的迭代器无法使用->去得到其成员。
Ptr operator->()
{
return &this->m_pnode->m_val;
}
🐱?👓总结
到了这里可能会有人会问为什么我们不写迭代器的拷贝构造函数和析构函数呢? 答:这是因为我们的迭代器是用来遍历容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址,并且这些结点元素析构和拷贝并不归我们管,结点应该归我们的list管,所以编译器默认提供的浅拷贝就已经足够了。
🎓容量相关函数
🐱?🐉empty()
- 功能:是用来获取容器中是否为空。
- 返回值:bool。
bool empty() const
{
return this->begin() == this->end();
}
🎃size()
- 功能:是用来获取元素的个数。
- 返回值:size_t(无符号整型)。
- 注意:但是在链表中这个接口并不常用。如果频繁的调用该接口会造成性能的下降。
size_t size() const
{
size_t size = 0;
const_iterator it = this->begin();
while(it!=this->end())
{
++it;
++size;
}
return size;
}
🎓元素访问相关函数
🎋back()
- 功能:获取最后一个元素的值。
- 返回值:存储的类型的引用。
T& back()
{
assert(!this->empty());
return this->m_head->m_prev->m_val;
}
const T& back() const
{
assert(!this->empty());
return this->m_head->prev->m_val;
}
🎑front()
- 功能:获取第一个元素的值。
- 返回值:存储的类型的引用。
T& front()
{
assert(!this->empty());
return this->m_head->m_next->m_val;
}
const T& front() const
{
assert(!this->empty());
return this->m_head->m_next->m_val;
}
🎓修改相关函数
🥙push_back()
- 功能:在尾部插入一个元素。
- 返回值:void(无返回值)。
void push_back(const T& val)
{
node* tail = this->m_head->m_prev;
node* newnode = new node(val);
newnode->m_next = tail->m_next;
newnode->m_prev = tail;
tail->m_next = newnode;
this->m_head->m_prev = newnode;
}
🐱?🚀push_front()
- 功能:在头部插入一个元素。
- 返回值:void(无返回值)。
void push_front(const T& val)
{
node* newnode = new node(val);
node* next = this->m_head->m_next;
newnode->m_next = next;
this->m_head->m_next = newnode;
newnode->m_prev = this->m_head;
next->m_prev = newnode;
}
🚩pop_back()
- 功能:在尾部删除一个元素。
- 返回值:void(无返回值)。
void pop_back()
{
assert(!empty());
node* tail = this->m_head->m_prev;
node* prev = tail->m_prev;
this->m_head->m_prev = prev;
prev->m_next = this->m_head;
tail->m_next = tail->m_prev = nullptr;
tail->m_val = T();
delete tail;
}
🐱?🏍pop_front()
- 功能:在头部删除一个元素。
- 返回值:void(无返回值)。
void pop_front()
{
assert(!empty());
node* delnode = this->m_head->m_next;
node* next = delnode->m_next;
this->m_head->m_next = next;
next->m_prev = this->m_head;
delnode->m_next = delnode->m_prev = nullptr;
delnode->m_val = T();
delete delnode;
}
🎐insert()
在c++98中我们的insert()函数有如下三种版本:  下面我们就将其模拟实现:
指定位置插入一个元素
- 功能:在指定位置插入一个元素。
- 返回值:插入的元素的位置的迭代器。
iterator insert(iterator pos, const T& val)
{
assert(pos.m_pnode != nullptr);
node* newnode = new node(val);
node* cur = pos.m_pnode;
node* prev = cur->m_prev;
newnode->m_next = cur;
newnode->m_prev = prev;
prev->m_next = newnode;
cur->m_prev = newnode;
return iterator(newnode);
}
指定位置插入n个相同的元素
- 功能:在指定位置插入一个元素。
- 返回值:void(无返回值)。
void insert(iterator pos, size_t n, const T& val)
{
assert(pos.m_pnode != nullptr);
while (n--)
{
node* newnode = new node(val);
node* cur = pos.m_pnode;
node* prev = cur->m_prev;
newnode->m_prev = prev;
prev->m_next = newnode;
newnode->m_next = cur;
cur->m_prev = newnode;
}
}
void insert(iterator pos, int n, const T& val)
{
assert(pos.m_pnode != nullptr);
assert(n > 0);
while (n--)
{
node* newnode = new node(val);
node* cur = pos.m_pnode;
node* prev = cur->m_prev;
newnode->m_prev = prev;
prev->m_next = newnode;
newnode->m_next = cur;
cur->m_prev = newnode;
}
}
void insert(iterator pos, long n, const T& val)
{
assert(pos.m_pnode != nullptr);
assert(n > 0);
while (n--)
{
node* newnode = new node(val);
node* cur = pos.m_pnode;
node* prev = cur->m_prev;
newnode->m_prev = prev;
prev->m_next = newnode;
newnode->m_next = cur;
cur->m_prev = newnode;
}
}
注意:这里的insert在指定位置插入为什么要实现三种重载版本呢?这与上面的构造函数问题是相同类型的,都是与模板区间构造有关,这里就不多赘述了。 指定位置插入区间内的元素
- 功能:在指定位置插入区间内的元素。
- 返回值:void(无返回值)。
- 注意:
-
template <class InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last)
{
assert(pos.m_pnode != nullptr);
while (first != last)
{
node* newnode = new node(*first);
node* cur = pos.m_pnode;
node* prev = cur->m_prev;
newnode->m_prev = prev;
prev->m_next = newnode;
newnode->m_next = cur;
cur->m_prev = newnode;
++first;
}
}
🎍erase()
在C++98中erase()有两种版本,一个是删除指定位置,另一个是删除区间内的元素。如下图所示:  删除指定位置
- 功能:删除指定位置的元素。
- 返回值:删除指定位置的元素的下一个结点的迭代器。
iterator erase(iterator pos)
{
assert(pos.m_pnode != nullptr);
assert(pos != end());
node* next = pos.m_pnode->m_next;
node* prev = pos.m_pnode->m_prev;
prev->m_next = next;
next->m_prev = prev;
pos.m_pnode->m_next = pos.m_pnode->m_prev = nullptr;
pos.m_pnode->m_val = T();
delete pos.m_pnode;
return iterator(next);
}
删除区间内的元素
- 功能:删除区间内的元素。
- 返回值:删除区间内的元素的下一个结点的迭代器。也就是返回last迭代器这个位置。
- 注意:
-
iterator erase(iterator first, iterator last)
{
node* prev = first.m_pnode->m_prev;
node* next = last.m_pnode;
while (first != last)
{
node* cur = first.m_pnode;
++first;
cur->m_next = cur->m_prev = nullptr;
cur->m_val = T();
delete cur;
cur = nullptr;
}
prev->m_next = next;
next->m_prev = prev;
return iterator(next);
}
💸clear()
- 功能:用于清空我们list中的所有元素的值,但不是要把list对象也删除。
- 返回值:void(无返回值)。
void clear()
{
iterator it = this->begin();
while (it != this->end())
{
++it;
this->pop_front();
}
}
🧨swap()
- 功能:
swap 顾名思义就是交换的意思,这里我们将这个swap作为我们的成员函数,用来交换两个list链表。 - 返回值: void(无返回值)。
void swap(list<T>& obj)
{
node* temp = this->m_head;
this->m_head = obj.m_head;
obj.m_head = temp;
}
🍜完整代码如下
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
namespace ZJ
{
template<class T>
class list_node
{
public:
list_node(T val=T())
:m_val(val)
,m_prev(nullptr)
,m_next(nullptr)
{}
public:
T m_val;
list_node<T>* m_prev;
list_node<T>* m_next;
};
template<class T,class Ref,class Ptr>
class list_iterator
{
public:
typedef list_node<T> node;
typedef list_iterator<T, Ref, Ptr>myself;
list_iterator(node* pnode)
:m_pnode(pnode)
{}
Ref operator* ()
{
return this->m_pnode->m_val;
}
Ptr operator->()
{
return &this->m_pnode->m_val;
}
bool operator!=(const myself&obj) const
{
return this->m_pnode != obj.m_pnode;
}
bool operator==(const myself& obj) const
{
return this->m_pnode == obj.m_pnode;
}
myself& operator++()
{
this->m_pnode = this->m_pnode->m_next;
return *this;
}
myself operator++(int)
{
const myself temp(*this);
this->m_pnode = this->m_pnode->m_next;
return temp;
}
myself& operator--()
{
this->m_pnode = this->m_pnode->m_prev;
return *this;
}
myself operator--(int)
{
const myself temp(*this);
this->m_pnode = this->m_pnode->m_prev;
return temp;
}
public:
node* m_pnode;
};
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;
public:
list()
{
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
}
list(size_t size, const T& val=T())
{
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
while (size--)
{
this->push_back(val);
}
}
list(long size, const T& val = T())
{
assert(size > 0);
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
while (size--)
{
this->push_back(val);
}
}
list(int size, const T& val = T())
{
assert(size > 0);
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
while (size--)
{
this->push_back(val);
}
}
template <class InputIterator>
list(InputIterator first, InputIterator last)
{
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
while (first != last)
{
node* newnode = new node(*first);
node* tail = this->m_head->m_prev;
tail->m_next = newnode;
newnode->m_prev = tail;
newnode->m_next = this->m_head;
this->m_head->m_prev = newnode;
++first;
}
}
list(const list<T>& obj)
{
this->m_head = new node(T());
this->m_head->m_next = this->m_head;
this->m_head->m_prev = this->m_head;
const_iterator it = obj.begin();
while (it != obj.end())
{
node* newnode = new node(it.m_pnode->m_val);
node* tail = this->m_head->m_prev;
tail->m_next = newnode;
newnode->m_prev = tail;
newnode->m_next = this->m_head;
this->m_head->m_prev = newnode;
++it;
}
}
list<T>& operator=(const list<T>& obj)
{
if (this != &obj)
{
list<T>temp(obj);
this->swap(temp);
}
return *this;
}
~list()
{
iterator it = this->begin();
while (it != this->end())
{
++it;
this->pop_front();
}
delete this->m_head;
this->m_head = nullptr;
}
iterator begin()
{
return iterator(this->m_head->m_next);
}
const_iterator begin() const
{
return const_iterator(this->m_head->m_next);
}
iterator end()
{
return iterator(this->m_head);
}
const_iterator end() const
{
return const_iterator(this->m_head);
}
bool empty() const
{
return this->begin() == this->end();
}
size_t size() const
{
size_t size = 0;
const_iterator it = this->begin();
while(it!=this->end())
{
++it;
++size;
}
return size;
}
T& back()
{
assert(!this->empty());
return this->m_head->m_prev->m_val;
}
const T& back() const
{
assert(!this->empty());
return this->m_head->prev->m_val;
}
T& front()
{
assert(!this->empty());
return this->m_head->m_next->m_val;
}
const T& front() const
{
assert(!this->empty());
return this->m_head->m_next->m_val;
}
void push_front(const T& val)
{
node* newnode = new node(val);
node* next = this->m_head->m_next;
newnode->m_next = next;
this->m_head->m_next = newnode;
newnode->m_prev = this->m_head;
next->m_prev = newnode;
}
void push_back(const T& val)
{
node* tail = this->m_head->m_prev;
node* newnode = new node(val);
newnode->m_next = tail->m_next;
newnode->m_prev = tail;
tail->m_next = newnode;
this->m_head->m_prev = newnode;
}
iterator insert(iterator pos, const T& val)
{
assert(pos.m_pnode != nullptr);
node* newnode = new node(val);
node* cur = pos.m_pnode;
node* prev = cur->m_prev;
newnode->m_next = cur;
newnode->m_prev = prev;
prev->m_next = newnode;
cur->m_prev = newnode;
return iterator(newnode);
}
void insert(iterator pos, size_t n, const T& val)
{
assert(pos.m_pnode != nullptr);
while (n--)
{
node* newnode = new node(val);
node* cur = pos.m_pnode;
node* prev = cur->m_prev;
newnode->m_prev = prev;
prev->m_next = newnode;
newnode->m_next = cur;
cur->m_prev = newnode;
}
}
void insert(iterator pos, int n, const T& val)
{
assert(pos.m_pnode != nullptr);
assert(n > 0);
while (n--)
{
node* newnode = new node(val);
node* cur = pos.m_pnode;
node* prev = cur->m_prev;
newnode->m_prev = prev;
prev->m_next = newnode;
newnode->m_next = cur;
cur->m_prev = newnode;
}
}
void insert(iterator pos, long n, const T& val)
{
assert(pos.m_pnode != nullptr);
assert(n > 0);
while (n--)
{
node* newnode = new node(val);
node* cur = pos.m_pnode;
node* prev = cur->m_prev;
newnode->m_prev = prev;
prev->m_next = newnode;
newnode->m_next = cur;
cur->m_prev = newnode;
}
}
template <class InputIterator>
void insert(iterator pos, InputIterator first, InputIterator last)
{
assert(pos.m_pnode != nullptr);
while (first != last)
{
node* newnode = new node(*first);
node* cur = pos.m_pnode;
node* prev = cur->m_prev;
newnode->m_prev = prev;
prev->m_next = newnode;
newnode->m_next = cur;
cur->m_prev = newnode;
++first;
}
}
void pop_front()
{
assert(!empty());
node* delnode = this->m_head->m_next;
node* next = delnode->m_next;
this->m_head->m_next = next;
next->m_prev = this->m_head;
delnode->m_next = delnode->m_prev = nullptr;
delnode->m_val = T();
delete delnode;
}
void pop_back()
{
assert(!empty());
node* tail = this->m_head->m_prev;
node* prev = tail->m_prev;
this->m_head->m_prev = prev;
prev->m_next = this->m_head;
tail->m_next = tail->m_prev = nullptr;
tail->m_val = T();
delete tail;
}
iterator erase(iterator pos)
{
assert(pos.m_pnode != nullptr);
assert(pos != end());
node* next = pos.m_pnode->m_next;
node* prev = pos.m_pnode->m_prev;
prev->m_next = next;
next->m_prev = prev;
pos.m_pnode->m_next = pos.m_pnode->m_prev = nullptr;
pos.m_pnode->m_val = T();
delete pos.m_pnode;
return iterator(next);
}
iterator erase(iterator first, iterator last)
{
node* prev = first.m_pnode->m_prev;
node* next = last.m_pnode;
while (first != last)
{
node* cur = first.m_pnode;
++first;
cur->m_next = cur->m_prev = nullptr;
cur->m_val = T();
delete cur;
cur = nullptr;
}
prev->m_next = next;
next->m_prev = prev;
return iterator(next);
}
void clear()
{
iterator it = this->begin();
while (it != this->end())
{
++it;
this->pop_front();
}
}
void swap(list<T>& obj)
{
node* temp = this->m_head;
this->m_head = obj.m_head;
obj.m_head = temp;
}
private:
node* m_head;
};
}
 如有错误之处,还请各位指出,谢谢大家!!! END…
|