目录
一、list模拟实现思路
二、节点类的实现
三、list迭代器的实现
1._list_iterator类
2.构造函数
3.operator* 运算符重载
4.operator-> 运算符重载
5.operator!= 运算符重载
6.operator==运算符重载
7.前置++
8.后置++
9.前置--
10.后置--
四、list类的实现
1.list类
2.构造函数
3.拷贝构造
4.赋值运算符重载
(1)传统的赋值运算符重载
(2)现代的赋值运算符重载
5.析构
6.迭代器
7.insert( )
8.erase( )
9.clear( )
10.push_front( )
11.push_back( )
12.pop_front( )
13.pop_back( )
14.empty( )
15 size( )
五、完整代码段
list链表的模拟实现,与前面的string和vector相比,略复杂一点:
(1)由于链表节点本身就是一个结构体,包含数据域、指针域。 将节点的相关操作放入节点类进行处理,逻辑更清晰
(2)string和vector的元素在空间上是连续分布的,迭代器++就能指向下一个元素,但list的迭代器不行,它的每个元素在空间上都不连续,要访问下一个节点必须找到当前节点的next,因此list的迭代器必须重写
链表主要模拟实现以下功能:
为了和库里面的list区分开,使用命名空间delia将 list类和库里list隔离开
namespace delia
{
}
节点类的成员变量有3个:
(1)节点值_val
(2)指向前一个节点的指针_prev
(3)指向后一个节点的指针_next
节点无需拷贝构造、赋值运算符重载,由于没有额外申请空间,因此也不需要析构
template
struct _list_node
{
//3个成员变量
T _val;
_list_node* _next;
_list_node* _prev;
//构造函数
_list_node(const T& val = T())
:_val(val)
, _prev(nullptr)
, _next(nullptr)
{};
};
由于节点的指针原生行为不满足迭代器定义,在这里,迭代器通过类来封装节点的指针重载运算符,如operator*、operator->、operator++等。用迭代器封装节点指针的思路不关心容器底层结构是数组、链表还是树等数据结构,都封装隐藏了底层细节,可以用一种统一的方式去访问、修改容器。
迭代器分为普通迭代器和const迭代器,对于_list_iterator类要实现两个版本,一个是普通的_list_iterator,另一个是const版本的_list_const_iterator,区别在于:对于两个类中的部分函数有普通函数和const函数之分(如begin( )和end( )),其他并无区别。因为这两个类的大部分代码相似,会造成代码冗余,如何解决呢?
对于T&,类模板实例化出两个类,一个是T&类,一个是const T&类,同理,T*也一样。使用
template
类模板就会实例化出来两个类,一个是普通的不带const的T,T&, T*,另一个是带const的T,const T&, const T*,其中Ref是引用,Ptr是指针,该类模板实例化了以下这两个类模板:
template _list_iterator _iterator;
template _list_iterator const_iterator;
这就解决了需要写两个类的问题。
迭代链表的节点,只需要一个成员变量即节点
template
struct _list_iterator//使用_list_iterator类来封装node*
{
typedef _list_node node;
typedef _list_iterator self;
node* _pnode;//成员变量
}
只需要初始化节点即可
//构造函数
_list_iterator(node* pnode)
:_pnode(pnode)
{}
拷贝构造、赋值运算符重载、析构函数,编译器默认生成即可
//解引用,返回左值,是拷贝,因此要用传引用返回
Ref operator*()
{
return _pnode->_val;
}
//结构体指针解引用,返回节点值的指针
Ptr operator->()
{
return &_pnode->_val;
}
//!=重载
bool operator!=(const self& s) const
{
return _pnode != s._pnode;
}
//==重载
bool operator==(const self& s) const
{
return _pnode == s._pnode;
}
让迭代器走到下一个节点
//前置++ it.operator(&it)
self& operator++()
{
_pnode = _pnode->next;
return *this;
}
//后置++ 返回++之前的值 it.operator(&it,0)
self operator++(int)//需构造临时对象,临时对象不能用引用返回,所以self没有加&
{
self tmp(*this);
_pnode = _pnode->_next;
return tmp;
}
让迭代器走到前一个节点
//前置-- it.operator(&it)
self& operator--()
{
_pnode = _pnode->prev;
return *this;
}
//后置-- 返回--之前的值 it.operator(&it,0)
self operator--(int)//需构造临时对象,临时对象不能用引用返回,所以self没有加&
{
self tmp(*this);
_pnode = _pnode->_prev;
return tmp;
}
list的成员只需要一个头节点,通过迭代器访问其他节点元素
template
class list
{
typedef _list_node node;
public:
typedef _list_iterator iterator;//重命名迭代器
typedef _list_iterator const_iterator;//重命名const迭代器
private:
node* _head;
};
list()
{
_head = new node;//会调_list_node的构造函数
_head->_next = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
_head->_prev = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
}
创建一个空链表,再把参数链表节点的值一个一个尾插进去
//拷贝构造 lt1(lt)
list(const list& lt)
{
//1.先构造一个只有头节点的空list:创建头节点,头节点的前一个是自己,头节点的后一个是自己
_head = new node;
_head->_next = _head;
_head->_prev = _head;
//2.将lt的元素全部尾插到新链表
for (const auto& e : lt)
{
push_back(e);
}
}
//赋值运算符重载 lt1 = lt 传统写法
list operator=(const list& lt)
{
//链表已存在,只需将节点尾插进去即可
if(this != lt)
{
for (auto& e : lt)
{
push_back(e);
}
}
}
list& operator=(list& lt)
{
swap(_head, lt->_head);
return *this;
}
其中,swap( )函数的执行过程如下:
template void swap ( T& a, T& b )
{
T c(a); a=b; b=c;
}
在执行赋值运算符重载时,会调用拷贝构造函数执行深拷贝,然后再交换两个链表头节点的指针,把this要释放的资源交给临时对象,临时对象出了swap的函数作用域就被this要释放的资源也会被同时释放。
(1)清除所有节点内容
(2)删除头节点,链表就销毁了
//析构
~list()
{
clear();//先清除所有节点内容
delete _head;//再删除头节点
_head = nullptr;
}
(1)普通迭代器
iterator begin()
{
return iterator(_head->_next);//头节点不存数据
}
iterator end()
{
return iterator(_head);//尾节点的下一个节点位置即头节点
}
(2)const迭代器
const_iterator begin() const
{
return const_iterator(_head->_next);//头节点不存数据
}
const_iterator end() const
{
return const_iterator(_head);//尾节点的下一个节点位置即头节点
}
(1)构造节点
//插入节点
void insert(iterator pos, const T& x)
{
assert(pos._pnode);
node* newnode = new node(x);//构造节点
node* prev = pos._pnode->_prev;//为方便后续操作,给插入位置的前一个节点起了个名字,pos是迭代器对象
//插入节点
newnode->_next = pos._pnode;
pos._pnode->_prev = newnode;
prev->_next = newnode;
newnode->_prev = prev;
}
(1)删除节点
(2)更新指针指向
//删除节点
iterator erase(iterator pos)
{
assert(pos._pnode);//判断该位置节点是否存在
assert(pos != end());//end()是最后一个节点的下一个节点位置,也就是头节点,头节点不能删,需要断言
node* prev = pos._pnode->_prev;//pos位置节点的前一个节点
node* next = pos._pnode->_next;//pos位置节点的后一个节点
//删除节点
delete pos._pnode;
prev->_next = next;
next->_prev = prev;
return iterator(next);//删除之后pos失效,把下一个位置的迭代器给它
}
只清除所有节点内容,不能删除头节点,如果删除头节点那么链表就不存在了,是链表的析构函数需要完成的操作
void clear()
{
iterator it = begin();
while (it != end())
{
erase(it++);//erase需要传参迭代器,即位置
}
}
头插
//头插
void push_front(const T& x)
{
insert(begin(), x);
}
尾插
//尾插
void push_back(const T& x)
{
insert(end()--, x);
}
头删
//头删
void pop_front()
{
erase(begin());
}
尾删
//尾删
void pop_back()
{
erase(--end());
}
判空
//判空
bool empty()
{
return end() == begin();
}
求节点个数
//求节点个数
size_t size()
{
iterator it = begin();
size_t sz = 0;
while (it != end())//时间复杂度O(N)
{
it++;
sz++;
}
return sz;
}
016-list.h
#pragma once
#include
#include
using namespace std;
namespace delia
{
template
struct _list_node
{
T _val;
_list_node* _prev;
_list_node* _next;
_list_node(const T& val = T())
:_val(val)
, _prev(nullptr)
, _next(nullptr)
{};
};
//节点的指针原生行为不满足迭代器定义,在这里,迭代器通过类来封装节点的指针重载运算符来控制
//对于T&,类模板实例化出两个类,一个是T&类,一个是const T&类,同理,T*也一样。
//使用template类模板就会实例化出来两个类,一个是T,T&, T*的,一个是T,const T&, const T*的
//template _list_iterator _iterator;
//template _list_iterator const_iterator;
//这样就不需要再去定义一个如下const的迭代器类:
//template
//struct _list_const_iterartor
//{
// typedef _list_node node;
// typedef _list_const_iterartor self;
// node* _pnode;
//}
//
template
struct _list_iterator//使用_list_iterator类来封装node*
{
typedef _list_node node;
typedef _list_iterator self;
node* _pnode;
//构造函数
_list_iterator(node* pnode)
:_pnode(pnode)
{}
//拷贝构造、赋值运算符重载、析构函数,编译器默认生成即可
//解引用,返回左值,是拷贝,因此要用引用返回
Ref operator*()
{
return _pnode->_val;
}
//结构体指针解引用,返回节点值的引用
Ptr operator->()
{
return &_pnode->_val;
}
//!=重载
bool operator!=(const self& s) const
{
return _pnode != s._pnode;
}
//==重载
bool operator==(const self& s) const
{
return _pnode == s._pnode;
}
//前置++ it.operator(&it)
self& operator++()
{
_pnode = _pnode->_next;
return *this;
}
//后置++ 返回++之前的值 it.operator(&it,0)
self operator++(int)
{
self tmp(*this);
_pnode = _pnode->_next;
return tmp;
}
//前置-- it.operator(&it)
self& operator--()
{
_pnode = _pnode->prev;
return *this;
}
//后置++ 返回++之前的值 it.operator(&it,0)
self operator--(int)//临时对象不能用引用返回,所以self没有加&
{
self tmp(*this);
_pnode = _pnode->_prev;
return tmp;
}
};
template
class list
{
typedef _list_node node;
public:
typedef _list_iterator iterator;//重命名迭代器
typedef _list_iterator const_iterator;//重命名const迭代器
//构造函数
list()
{
_head = new node;//会调_list_node的构造函数
_head->_next = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
_head->_prev = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
}
//拷贝构造 lt1(lt)
list(const list& lt)
{
//1.先构造一个只有头节点的空list:创建头节点,头节点的前一个是自己,头节点的后一个是自己
_head = new node;
_head->_next = _head;
_head->_prev = _head;
//2.将lt的元素全部尾插到新链表
for (const auto& e : lt)
{
push_back(e);
}
}
赋值运算符重载 lt1 = lt 传统写法
//list& operator=(list& lt)
//{
// if(this != lt)
// {
// for (auto& e : lt)
// {
// push_back(e);
// }
// }
//}
//赋值运算符重载 lt1 = lt 现代写法
list& operator=(list& lt)
{
swap(_head, lt._head);
return *this;
}
//析构
~list()
{
clear();
delete _head;
_head = nullptr;
}
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);//尾节点的下一个节点位置即头节点
}
//插入节点
void insert(iterator pos, const T& x)
{
assert(pos._pnode);
node* newnode = new node(x);//构造节点
node* prev = pos._pnode->_prev;//为方便后续操作,给插入位置的前一个节点起了个名字,pos是迭代器对象
//插入节点
newnode->_next = pos._pnode;
pos._pnode->_prev = newnode;
prev->_next = newnode;
newnode->_prev = prev;
}
//删除节点
iterator erase(iterator pos)
{
assert(pos._pnode);//判断该位置节点是否存在
assert(pos != end());//end()是最后一个节点的下一个节点位置,也就是头节点,头节点不能删,需要断言
node* prev = pos._pnode->_prev;//pos位置节点的前一个节点
node* next = pos._pnode->_next;//pos位置节点的后一个节点
//删除节点
delete pos._pnode;
prev->_next = next;
next->_prev = prev;
return iterator(next);//删除之后pos失效,把下一个位置的迭代器给它
}
void clear()
{
iterator it = begin();
while (it != end())
{
erase(it++);
}
}
//头插
void push_front(const T& x)
{
insert(begin(), x);
}
//尾插
void push_back(const T& x)
{
insert(end()--, x);
}
//头删
void pop_front()
{
erase(begin());
}
//尾删
void pop_back()
{
erase(--end());
}
//判空
bool empty()
{
return _head->_next == _head;
}
//求节点个数
size_t size()
{
iterator it = begin();
size_t sz = 0;
while (it != end())//时间复杂度O(N)
{
it++;
sz++;
}
return sz;
}
private:
node* _head;
};
void PrintList(const list& lt)
{
list::const_iterator it = lt.begin();
while (it != lt.end())
{
cout << it._pnode->_val << " ";
it++;
}
cout << endl;
}
void test_list1()
{
list l1;
l1.push_back(5);
l1.push_back(8);
l1.push_back(20);
l1.push_back(9);
PrintList(l1);
list l2;
l2 = l1;
PrintList(l2);
cout << endl;
}
}
016-test.cpp
#include "016-list.h"
#include
using namespace std;
int main()
{
delia::test_list1();
return 0;
}