list前言
1.list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代
2.list底层是双向链表结构
3.与其他的容器相比(array,vector,list),list通常在任意位置进行插入,移除元素的执行效率更好
4.缺陷是不支持任意位置的随机访问
head是哨兵结点,本身不存储数据,就是当头尾的作用。
list框架
1.list_node(结点)
template<class T>
struct list_node
{
T _data;//数据
list_node<T>* _next;//下一个结点
list_node<T>* _prev;//上一个
list_node(const T& x = T())//匿名对象(常属性,const修饰)
:_data(x)
,_next(nullptr)
,_prev(nullptr)
{
}
};
2.__list_ioterator(迭代器)
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++(int)//后置++
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
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;
}
};
1.为什么template<class T,class Ref,class Ptr>里面有三个
因为迭代器里面需要有<T, T& , T*>,而且迭代器需要const和非const,要不用class T,class Ref,class Ptr 就得写俩迭代器,还不如让一个封装。
2.关于Ptr operator->()的实现,实际上隐藏了一个 “->”
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())
{
//cout << (*it)._a1<<" "<<(*it)._a2 << endl;
cout << it->_a1 << " " << it->_a2 << endl;
cout << it.operator->()->_a1 << " " << it.operator->()->_a1 << endl;//实际上调用起来是这样隐藏了一个->
++it;
}
3.迭代器为什么要封装?
首先提问为什么list的迭代器需要自己写解引用和++,我们来看一下vector和string的迭代器,其底层是原生指针,且容器的空间是连续的,解引用和++可以直接拿到数据。
但对于list,它是不连续的,通过前后指针链接,++不能找到后一个结点,解引用拿到的是node结构体,不是里面的data。
所以我们需要将迭代器封装。
3.class list
template<class T>
class list
{
typedef list_node<T> Node;
public:
//typedef list_node<T> Node;
typedef __list_iterator<T,T&,T*> iterator;//两个迭代器
typedef __list_iterator<T,const T&,const T*> const_iterator;
private:
Node* _head;//头
size_t _size;//记录个数
};
观察list框架里面的两个迭代器,联系上面迭代器框架,完美体现了模板的好处
迭代器
const_iterator begin()const
{
return _head->_next;
}
const_iterator end()const
{
return _head;
}
iterator begin()
{
//return iterator(_head->_next);
return _head->_next;
}
iterator end()
{
return _head;
}
初始化和析构
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
empty_init();
}
void clear()清空链表
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
~list()
{
clear();
delete _head;//头节点也释放掉
_head = nullptr;
}
构造和拷贝构造函数
构造函数
//lt2(lt1)
list(const list<T>& lt)
{
empty_init();初始化
for (auto e : lt)//按顺序放
{
push_back(e);
}
}
拷贝构造函数(现代写法)
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
//lt2 = lt1
list<int>& operator=(list<int> lt)//让编译器替你构造然后交换
{
swap(lt);
return *this;
}
push_back和insert和erase
void push_back(const T& x)
{
Node* tail = _head->_prev;//记录尾
Node* newnode = new Node(x);//创建新
tail->_next = newnode;//四步指针插入过程
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
insert(往指定位置前面插入),insert后迭代器并不会失效,因为pos的指向并不会改变
void insert(iterator pos ,const T& val)
{
Node* cur = pos._node;//取出迭代器指向的node
Node* newnode = new Node(val);//创建新
Node* prev = cur->_prev;//记录插入pos的前一个
prev->_next = newnode;//四步指针插入过程
newnode->prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
}
erase删除之后迭代器会失效
迭代器失效包括迭代器所指向的结点失效,erase执行后,it所指向的结点被删除,it无效,在下一次使用时,必须先给it赋值
iterator erase(iterator pos)//迭代器失效:pos位置已经被释放掉了
{
//记录pos位置结点的前后节点
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
delete cur;//释放结点
prev->_next = next;
prev->_prev = prev;
--_size;
return iterator(next);
}
print_container()打印容器函数
首先我们来看print_list()函数
template<typename T>
void print_list(const list<T>& lt)
{
//list<T>未实例化的类模板,编译器不能去他里面去找
//编译器就无法确定const_iterator是内嵌类型,还是静态成员变量
//前面加一个typename就是告诉编译器,这是一个类型,等list<T>实例化后再去类里面取
typename list<T>::const_iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
1.为什么那个const_iterator前面要加一个typename?
因为list是未实例化的类模板,编译器没法进去找,编译器就无法知道const_iterator是内嵌类型,还是静态成员变量,前面加typename就是告诉编译器,这是一个类型,等list实例化后再去类里面取
2.弊端:它只能打印list,但是我们想要打印所有容器,所以就有了print_container()
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);
template<typename Container>
void print_container(const Container& con)
{
//list<T>未实例化的类模板,编译器不能去他里面去找
//编译器就无法确定const_iterator是内嵌类型,还是静态成员变量
//前面加一个typename就是告诉编译器,这是一个类型,等list<T>实例化后再去类里面取
typename Container::const_iterator it = con.begin();
while (it != con.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
总代码
namespace nzq
{
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)
{
}
};
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++(int)//后置++
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
self& operator--()
{
_node = _node->_prev;
return *this;
}
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>
class list
{
typedef list_node<T> Node;
public:
//typedef list_node<T> Node;
typedef __list_iterator<T,T&,T*> iterator;
typedef __list_iterator<T,const T&,const T*> const_iterator;
const_iterator begin()const
{
return _head->_next;
}
const_iterator end()const
{
return _head;
}
iterator begin()
{
//return iterator(_head->_next);
return _head->_next;
}
iterator end()
{
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);
}
}
//lt2 = lt1;
/*list<T>& operator=(const list<T>& 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);
}
//lt2 = lt1
list<int>& operator=(list<int> lt)
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void push_back(const T& x)
{
Node* tail = _head->_prev;
Node* newnode = new Node(x);
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
iterator insert(iterator pos ,const T& val)
{
Node* cur = pos._node;
Node* newnode = new Node(val);
Node* prev = cur->_prev;
prev->_next = newnode;
newnode->prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
return newnode;
}
iterator erase(iterator pos)//迭代器失效:pos位置已经被释放掉了
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
delete cur;
prev->_next = next;
prev->_prev = prev;
--_size;
return iterator(next);
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
size_t size()const
{
return _size;
}
private:
Node* _head;
size_t _size;
};
//template<typename T>
//void print_list(const list<T>& lt)
//{
// //list<T>未实例化的类模板,编译器不能去他里面去找
// //编译器就无法确定const_iterator是内嵌类型,还是静态成员变量
// //前面加一个typename就是告诉编译器,这是一个类型,等list<T>实例化后再去类里面取
// typename list<T>::const_iterator it = lt.begin();
// while (it != lt.end())
// {
// cout << *it << " ";
// it++;
// }
// cout << endl;
//}
template<typename Container>
void print_container(const Container& con)
{
//list<T>未实例化的类模板,编译器不能去他里面去找
//编译器就无法确定const_iterator是内嵌类型,还是静态成员变量
//前面加一个typename就是告诉编译器,这是一个类型,等list<T>实例化后再去类里面取
typename Container::const_iterator it = con.begin();
while (it != con.end())
{
cout << *it << " ";
it++;
}
cout << endl;
}
void test()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
//封装屏蔽底层差异和实现细节
//提供统一的访问修改遍历方式
/*list<int>::iterator it = l1.begin();
while (it != l1.end())
{
cout << *it << " ";
++it;
}*/
print_container(l1);
}
}