节点的结构体
首先节点先套用一个模版,因为数据的数据类型可能是各种各样的自定义类型,或者是内置类型的
成员变量
一个data存储数据,next节点指向下一个节点,prev指向前一个节点,
构造函数
传一个T数据类型的x,其中x可能是任何数据类型的,可以不填值,只开空间,next和prev都是指向的空
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)
{}
};
迭代器
list的迭代器和string,vector的不同,string,vector的的数据存储是在连续的空间里面,而list需要用链接的地址去迭代,所以最好就是封装出一个迭代器去模拟指针的行走过程
模版
这边模版包含类型,ref代表引用该参数,Ptr代表指针
template<class T, class Ref, class Ptr>
定义类型
第一个数据类型是将list_node简写成Node,第二个是将该struct简写成self
struct _list_iterator{
typedef list_node<T> Node;
typedef _list_iterator<T,Ref,Ptr> self;
构造函数
创建一个node类型的指针的成员函数,然后我们给该结构体进行初始化列表初始化
Node* _node;
_list_iterator(Node* node)
:_node(node)
{}
operator++和operator--
++和--分别返回当前节点的下一个节点
self& operator++(){
_node=_node->_next;
return *this;
}
self& operator--(){
_node=_node->_prev;
return *this;
}
operator后置++和后置--
迭代器其实已经向后走了,但是返回的却是原迭代器的地址
self operator++(int){
self tmp(*this);
_node=_node->_next;
return tmp;
}
self operator--(int){
self tmp(*this);
_node=_node->_prev;
return tmp;
}
operator*
返回的是该迭代器指针指向的数据
T& operator*(){
return _node->_data;
}
operator==和!=
==比较两个指针是否相等,相等则为1,不相等则为0,!=反之
bool operator!=(const self& s){
return _node!=s._node;
}
bool operator==(const self& s){
return _node==s._node;
}
operator->
返回的是自定义或者
T* operator->(){
return &_node->_data;
}
创建一个自定义类型的struct,其中有两个成员变量a1和a2,并对其初始化
struct AA{
AA(int a1=0,int a2=0)
:_a1(a1),
_a2(a2)
{}
int _a1;
int _a2;
};
这边->等价于.operator-> ()->,operator->()调用该函数该函数返回的是node->data的地址,该地址再解引用返回_a1
list
模版
依旧采用和iterator一样的模版
template<class T, class Ref, class Ptr>
成员变量
一个是Node类型的节点,还有一个size记录链表长度
Node* _head;
size_t _size;
类型定义
我们让Node成为私有类型,因为我们不想让外部访问他,而interator和const_iterator可以进行外部访问
typedef list_node<T> Node;
public:
typedef _list_iterator<T,T&,T* > iterator;
typedef _list_iterator<T,const T&,const T*>const_iterator;
成员函数
构造函数
我们在这直接调用一个初始化函数来建造链表的头节点,头节点new一个Node类型,头节点和尾节点都指向自己,size=0;
void empty_init(){
_head=new Node;
_head->_next=_head;
_head->_prev=_head;
_size=0;
}
list(){
empty_init();
}
析构函数
先调用一个clear函数,clear函数中用一个迭代器去指向begin的位置,然后从该位置一直erase后链表清空,清空完后销毁head节点
void clear(){
iterator it=begin();
while(it!=end()){
it= erase(it);
}
}
~list(){
clear();
delete _head;
_head=nullptr;
}
拷贝构造函数
我们先调用初始化函数建造一个头节点,建完后遍历被拷贝的链表,进行尾插
list(list<T>& lt){
empty_init();
for(auto& e:lt){
push_back(e);
}
}
operator=
直接调用swap函数去交换,这边等于是一个传值穿参,所以可以直接交换
void swap(list<T>& lt){
std::swap(lt._head,_head);
std::swap(lt._size,_size);
}
list<T>& operator=(list<T> lt){
swap(lt);
return *this;
}
begin和end
begin和end都是用了iterator进行返回,其中begin返回的是head下一个位置,因为是双向循环带头链表,所以end就是head节点
iterator begin(){
return _head->_next;
}
iterator end(){
return _head;
}
insert
先创建cur节点指向it._node,其中it是iterator类型的指针,prev指向cur的前一个位置,我们在这个位置插入一个节点,插完后size++,返回的是把newnode强转成iterator类型指针,这个指针就是指向new node的地址,同样,返回的也是该地址。
iterator insert(iterator it,const T& x){
Node* cur=it._node;
Node* prev=cur->_prev;
Node* newnode=new Node(x);
prev->_next=newnode;
newnode->_prev=prev;
cur->_prev=newnode;
newnode->_next=cur;
++_size;
return iterator(newnode);
}
push_back
插入end前面的地址
void push_back(const T& x){
insert(end(),x);
}
push_front
头插就是在头部插入
void push_front(const T& x){
insert(begin(),x);
}
erase
创建三个迭代器分别指向前,当前,后节点,再释放当前节点后连接后返回下一个节点的迭代器指针
iterator erase(iterator it){
Node* cur=it._node;
Node* prev=cur->_prev;
Node* next=cur->_next;
delete[] cur;
prev->_next=next;
next->_prev=prev;
--_size;
return iterator(next);
// return next;
}
pop_back和pop_front
分别都直接调用erase函数去进行pop
void pop_back(){
erase(--end());
}
void pop_front(){
erase(begin());
}
size
直接返回即可
size_t size(){
return _size;
}
const_iterator
可以再写一个类去封装const_iterator,但是实现的功能和iterator差不多,所以,只要在iterator的基础上略做调整
传过去的时候是const类型的,就变成只读了。
typedef _list_iterator<T,const T&,const T*>const_iterator;
template<class T,class Ref,class Ptr >
const_iterator begin()const{
return (const_iterator)(_head->_next);
}
const_iterator end()const{
return (const_iterator)(_head);
}
typename
list<T>未实例化模版,编译器不敢进去找,编译器不确定list<T>::const_iterator是内嵌类型还是静态成员变量 加typename告诉编译器,这是个类型,等list<T>实例化后再去类里去取