第一步:看源代码
类的框架:
成员函数:
基本可以确定list是一个带头双向循环链表,end()的迭代器指向头节点,begin()的迭代器指向头结点的下一个节
list的迭代器:(稍显复杂)
库中的迭代器成员函数只有一个链表节点指针。对链表节点指针直接解引用只能拿到节点空间,没办法拿到节点中存储的date,但是可以通过自定义类型封装间接实现拿到date。节点的指针++,也不会到达下一个地址。自定义类型的运算符重载可以控制行为。
下面是具体的实现:
list.h中
#pragma once
#include<iostream>
#include<algorithm>
#include<assert.h>
using namespace std;
namespace bit
{
template<class T>
struct _list_node
{
_list_node(const T& x = T())
//这里的缺省不能直接给0,因为T的类型可能为string,vector
{
_date = x;
_next = nullptr;
_prev = nullptr;
}
_list_node<T>* _next;
_list_node<T>* _prev;
T _date;
};
//list的迭代器是一个难点
//template<class T>最开始的框架
//typedef _list_iterator<T, T&, T*> iterator;
//typedef _list_iterator<T, const T&, const T*> const_iterator;
//当调用begin()的类型不同时匹配的begin()函数不同,决定了是返回const_iterator类型还是iterator类型,
//由此决定ref具体是什么类型,ptr具体是什么类型。
template<class T, class ref, class ptr>
//源代码中模板参数有三个template<class T, class Ref, class Ptr>这个和const迭代器有关。
struct _list_iterator
{
typedef _list_node<T> Node;
typedef _list_iterator<T,ref,ptr> Self;
Node* _node;
//迭代器器的本质是一个节点指针,和_head一样。
_list_iterator(Node* it)
//如果不想被隐式类型转换可以这样声明:explicit _list_iterator(Node* it)
:_node(it)
{}
//需要写析构函数吗?
//不需要,_node指向的是节点空间,不能说使用一次迭代器就释放节点空间,所以使用默认生成的析构函数就可以
//默认生成的析构函数不会对指针类型进行处理
//需要写拷贝构造和赋值重载吗?
//不需要,指针为内置类型,默认生成的浅拷贝就可以。
ref operator*()
//开始的框架:T& operator*()
{
return _node->_date;
//_date是节点中的数据,节点是new出来的,出作用域不会消失,可以引用返回。
}
//开始的框架:T* operator->()
ptr operator->()
//
ptr
operator->
()是干什么的?
//当T的实参为类时会用到。在下面test.cpp中test2有示范
{
return &(_node->_date);
}
bool operator==(const Self& pos)
{
return _node == pos._node;
}
bool operator!=(const Self& pos)
//pos是_list_iterator<T>类型
{
return _node != pos._node;
//这里必须用_node进行比较,如果用迭代器比较,会再进行!=重载,形成死循环。
//this是指向迭代器的指针,是_list_iterator<T>*类型
}
Self& operator++()
{
_node = _node->_next;
return *this;
//_node是_list_node<T>*类型,不能转换为_list_iterator<T>&类型
//如果没有&,就可以,因为支持单参数隐式类型转换。
}
Self operator++(int)
{
Self tmp = _node;
_node = _node->_next;
return tmp;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
Self operator--(int)
{
Self tmp = _node;
_node = _node->_prev;
return tmp;
}
};
template<class T>
class list
{
typedef _list_node<T> Node;
//重复typedef是因为上一个typedef只有在所在的{}内才有效
public:
//开始的框架:typedef _list_iterator<T> iterator;
typedef _list_iterator<T,T&,T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;
//类模板给不同的参数,会变成不同的类型
//关于为什么const迭代器中T的前面不用加const:加上const,迭代器中的T就表示const T,_list_node<T>就表示
//_list_node<const T>list存储的数据变成不可修改的了,而Node中的内容是可以修改的。类型不匹配,
//在类模板中不同类型之间是不能赋值的(有const和没有const是不同类型),会报错。
//迭代器封装以后,在使用时(不同容器的迭代器)看起来是一样的。如果没有被public修饰,就无法在list外使用。
//T是存储的数据类型,T&/const T&是返回存储的数据,加引用是为了减少拷贝,
//有无const取决于可不可以通过迭代器修改数据。
//关于迭代器使用的图示:
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
//传统写法:
//list(const list<T>& it)//it2(it1)
//{
// _head = new Node;
// _head->_next = _head;
// _head->_prev = _head;
// for (auto e : it)
// {
// push_back(e);
// }
//}
//现代写法:
void empty_init()
//库中也提供了这个函数
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
template <class InputIterator>
list(InputIterator first, InputIterator last)
{
empty_init();
while (first != last)
{
push_back(*first);
first++;
}
}
void swap(list<T>& it)
//it的内容要修改,不能加const
{
std::swap(_head, it._head);
//依据实现size()的方式,可能还有交换size,这里没有实现size(),所以没交换。
}
list(const list<T>& it)
{
empty_init();
//不能用未初始化的list和tmp交换。
list<T> tmp(it.begin(), it.end());
swap(tmp);
}
list<T>& operator=(list<T> it)
//it2 = it1
{
swap(it);
//传值拷贝,it中已经有需要的数据了
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)
{
///*Node* tmp = new Node(x);
//_head->_prev->_next = tmp;
//tmp->_prev = _head->_prev;
//tmp->_next = _head;
//_head->_prev = tmp;*/
//Node* tmp = new Node(x);
//Node* tail = _head->_prev;//保存尾节点的地址,就可以不用按顺序完成链表的连接了
//tail->_next = tmp;
//tmp->_prev = tail;
//tmp->_next = _head;
//_head->_prev = tmp;
insert(end(), x);
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_back()
{
erase(--end());
//这里可以用--end(),原因不明
}
void pop_front()
{
erase(begin());
}
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);
}
iterator insert(iterator pos, const T& val)
//在pos位置之前插入数据
{
Node* newnode = new Node(val);
Node* cur = pos._node;
cur->_prev->_next = newnode;
newnode->_prev = cur->_prev;
newnode->_next = cur;
cur->_prev = newnode;
return iterator(newnode);
//insert的返回值很少用,和库里尽量保持一致还是加上了。
}
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
return iterator(next);
//返回值解决迭代器失效的问题
}
private:
Node* _head;
};
}
test.cpp中
#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
#include<list>
void test_list1()
{
bit::list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
l.push_back(5);
bit::list<int>::iterator it = l.begin();
while (it != l.end())
{
cout << *it << " ";
++it;
}
}
struct AA
{
AA(int a = 0, int b = 0)
{
a1 = a;
a2 = b;
}
int a1;
int a2;
};
cout没有AA的重载的解决方案一:加上重载
//ostream& operator<<(ostream& out, AA& aa)//函数中不能定义函数
//{
// cout << aa.a1 << " " << aa.a2 << " ";
// return out;
//}
void test_list2()
{
bit::list<AA> l;
l.push_back(AA(1, 1));
l.push_back(AA(2, 2));
l.push_back(AA(3, 3));
l.push_back(AA(4, 4));
l.push_back(AA(5, 5));
bit::list<AA>::iterator it = l.begin();
/*while (it != l.end())
{
cout << *it << " ";//这里的it是迭代器,解引用后拿到的是_date,_date的类型是T,即AA。cout没有AA的重载
++it;
}*/
while (it != l.end())
{
cout没有AA的重载的解决方案二:
//cout << (*it).a1 << " " << (*it).a2 << " ";
//it是迭代器,迭代器模仿的是指针,指针有通过->直接访问数据的功能,而it是一个类
//cout << it->a1 << " " << it->a2 << " ";//原本应该这样使用的:it._node->_date.a1
//库中支持迭代器直接访问数据,库中函数:T* operator->() const { return &(operator*()); }
//it调用operator*(),*it会拿到其中的数据,即拿到_next->date,为operator*()的返回值;
//it调用operator->()返回&(_node->date),即AA*,it->a1即AA* a1,实际上编译器为了可读性对这里进行简化。
//it->a1实际上应该是it->->a1。it->即:it.operator->()返回AA*,AA*->a1,读取到数据
cout << it->a1 << " " << it->a2 << " ";
++it;
}
}
void print_list(const bit::list<int> tmp)
{
bit::list<int>::const_iterator it = tmp.begin();
//如果只是在begin()后加上const的修饰,只能编译通过,不能保证数据不可修改
//要能做到不可修改,需要给 * 和 -> 的重载返回值加上const,但是加上const同时也意味着非const修饰的迭代器不可修改
// 一般情况下,会选择再写一个_list_const_iterator类型的类,但是和_list_iterator类的逻辑高度重合,维护起来不方便
// 下面跳转到list类部分,见证大佬为何是大佬。(每个注释有开始的框架的上下行都是大佬智慧的结晶)
// 还不理解的翻到末尾,有图解说明
//标准库中begin()可以自动识别迭代器类型,但是不能将const修饰的迭代器赋值给非const修饰的迭代器。
//自定义类型被const修饰和不被const修饰是两个无关类型,
//不能将被const修饰的类型赋值给不被const修饰的类型,标准库中也一样
//比如说自定义类型的迭代器,不能说用const修饰了,就能支持const类型运算,还要有const类型对应运算的重载,
//就算不被const修饰类型已经有对应的运算重载也不行。
while (it != tmp.end())
{
cout << *it << " ";
++it;
}
}
void test_list3()
{
bit::list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
l.push_back(5);
print_list(l);
}
void test_list4()
{
bit::list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
l.push_back(5);
l.insert(l.begin(), 10);
l.push_front(20);
l.push_front(30);
l.push_front(40);
l.erase(l.begin());
l.pop_back();
l.pop_front();
bit::list<int>::iterator it = l.begin();
while (it != l.end())
{
cout << *it << " ";
++it;
}
}
void test_list5()
//关于list的insert的迭代器失效问题
{
bit::list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(2);
l.push_back(3);
l.push_back(4);
l.push_back(5);
l.push_back(6);
bit::list<int>::iterator it = l.begin();
while (it != l.end())
{
if (*it % 2 == 0)
{
l.insert(it, *it * 10);
}
it++;
}
for (auto it : l)
{
cout << it << " ";
}
cout << endl;
//运行发现没什么问题
//原因在于:list空间不连续,insert插入数据不需要挪动数据,所以不存在野指针问题,
//insert插入数据,插入在指向的节点之前,意义也没有发生变化。
}
void test_list6()
//关于list的erase的迭代器失效问题
{
bit::list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(2);
l.push_back(3);
l.push_back(4);
l.push_back(5);
l.push_back(6);
bit::list<int>::iterator it = l.begin();
/*while (it != l.end())
{
if (*it % 2 == 0)
{
l.erase(it);//这样写是必然崩溃的,it都被释放了,it迭代器自然失效
}
it++;
}*/
while (it != l.end())
{
if (*it % 2 == 0)
{
it = l.erase(it);
}
else
{
it++;
}
}
for (auto it : l)
{
cout << it << " ";
}
cout << endl;
l.clear();
l.push_back(1);
l.push_back(2);
l.push_back(3);
for (auto it : l)
{
cout << it << " ";
}
cout << endl;
}
void test_list7()
{
bit::list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
l.push_back(5);
l.push_back(6);
bit::list<int> l2(l);
for (auto it : l)
{
cout << it << " ";
}
cout << endl;
l.pop_back();
l2 = l;
for (auto it : l2)
{
cout << it << " ";
}
}
int main()
{
test_list7();
return 0;
}