一.源码分析


1.成员变量

链表里面存储的就是节点的指针
2.构造函数



我们就可以知道,list是双向链表

3.push_back()函数分析


在position位置之前插入节点
二.定义成员函数
cpp
namespace ltw
{
template<class T>
struct ListNode
{
ListNode<T>* _next;
ListNode<T>* _prev;
T _data;
};
template<class T>
class List
{
typedef ListNode<T> Node;
public:
List()
{
head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
private:
Node* _head;
};
}

三.实现push_back()函数


cpp
void push_back(const T& x)
{
Node* newnode = new Node(x);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
我们在使用模板的时候,在编译的时候是不会细节检查的,只有实例化(不实例化也不会报错),才会去检查



按需实例化(不调用就不实例化这个成员函数)
所以我们要给push_back()提供一个ListNode(const T& data)的构造函数
cpp
template<class T>
struct ListNode
{
ListNode<T>* _next;
ListNode<T>* _prev;
T _data;
ListNode(const T& data)
:_next(nullptr)
,_prev(nullptr)
,_data(data)
{
}
};
我们这里使用_next和_prev很频繁,所以我们对于整个类使用struct(全部用公有)

cpp
List()
{
head = new Node(T());
_head->_next = _head;
_head->_prev = _head;
}
所以我们还不如直接将上面的写成全缺省的类型,代码如下:
cpp
namespace ltw
{
template<class T>
struct ListNode
{
ListNode<T>* _next;
ListNode<T>* _prev;
T _data;
ListNode(const T& data = T())
:_next(nullptr)
,_prev(nullptr)
,_data(data)
{
}
};
template<class T>
class List
{
typedef ListNode<T> Node;
public:
List()
{
head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
void push_back(const T& x)
{
Node* newnode = new Node(x);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
private:
Node* _head;
};
}
四.遍历list(重点)
1.迭代器的实现
我们不能使用前两个数据结构的方法 typedef Node* iterator (因为++没办法取到我们的下一个节点的)
我们重新设计一个类,来实现迭代器的功能
cpp
template<class T>
class ListIterator
{
typedef ListNode<T> Node;
Node* _node;
};



cpp
template<class T>
class ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T> Self;
Node* _node;
ListIterator(Node* node)
:_node(node)
{
}
// ++it
Self& operator++()
{
_node = _node->_next;
return *this;
}
T& operator*()
{
return _node->_data;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
};
迭代器类的主要实现就是, ++ , * , != 这三个运算符,我们就先写这三个
2.在List里面引入迭代器
1.begin()的实现
cpp
typedef ListIterator<T> iterator;
iterator begin()
{
iterator it(_head->_next);
return it;
}

2.end()的实现
cpp
iterator end()
{
return iterator(_head);
}
end()是最后一个元素的下一个位置,所以是我们的哨兵位
3.List类的总代码
cpp
template<class T>
class List
{
typedef ListNode<T> Node;
public:
typedef ListIterator<T> iterator;
iterator begin()
{
// iterator it(_head->_next);
// return it;
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
List()
{
head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
void push_back(const T& x)
{
Node* newnode = new Node(x);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
private:
Node* _head;
};

cpp
测试代码
void test_list1()
{
list<int> lt1;
// 按需实例化(不调用就不实例化这个成员函数)
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
lt1.push_back(5);
// Func(lt1);
//ListIterator<int> it = lt1.begin();
list<int>::iterator it = lt1.begin();
while (it != lt1.end())
{
*it += 10;
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
}


内置类型不能改变运算符的规则,但是我要是把内置类型进行封装,然后对封装的类进行重载,这样就间接的将我们的内置类型进行重载了
以后,对于红黑树,哈希表,我们都可以是要上述的方式进行操作,达到我们想要的效果
4.完善我们迭代器的运算符
1.重载++,--(前置和后置)
cpp
// ++it
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
Self& operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
2.operator==()
cpp
bool operator==(const Self& it)
{
return _node == it._node;
}
operator+效率太低了,我们就不重载了(库里面也没重载)
迭代器的节点是不用进行析构的,因为我们的节点不是单独开的,而是在链表里面的,(析构交给链表)
不要越级管理
3.拷贝构造(这个地方就是浅拷贝)
我们要拷贝的话,就是希望把我们的指针拷贝给你,默认的拷贝构造就够用
4.重载operator->
cpp
T* operator->()
{
return &_node->_data;
}
理解为啥要 重载operator->运算符?

我们要是想用,就能够重载 operator <<,但是我们也可以进行重载operator->
下面这种方式也能进行访问,但是不够方便
(*it) 表示的就是pos,pos用来访问struct内的x,y使用 .
(it) 表示pos的地址,地址访问里面的数据使用 ->

cpp
struct Pos
{
int _row;
int _col;
Pos(int row = 0, int col = 0)
:_row(row)
,_col(col)
{}
};
void test_list2()
{
list<Pos> lt1;
lt1.push_back(Pos(100, 100));
lt1.push_back(Pos(200, 200));
lt1.push_back(Pos(300, 300));
list<Pos>::iterator it = lt1.begin();
while (it != lt1.end())
{
//cout << (*it)._row << ":" << (*it)._col << endl;
// 为了可读性,省略了一个->
cout << it->_row << ":" << it->_col << endl;
//cout << it->->_row << ":" << it->->_col << endl;
cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;
++it;
}
cout << endl;
}
5.编译器的优化的解释:

具体的解释如下:


6.const变量的常性和临时变量的常性的区别






相当于是位于const和非const的一个中间态
7.const迭代器的生成


const迭代器,本身是可以修改的,但是指向的内容不能进行修改

因为const迭代器是值不能修改而指向能进行修改
cpp
template<class T>
class ListConstIterator
{
typedef ListNode<T> Node;
typedef ListConstIterator<T> Self;
Node* _node;
public:
ListConstIterator(Node* node)
:_node(node)
{}
// ++it;
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
Self operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
const T& operator*()
{
return _node->_data;
}
//const T* it --> 修饰的是T,如int不能被修改,但是指向能修改(我们的迭代器还是能修改的)
const T* operator->()
{
return &_node->_data;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
bool operator==(const Self& it)
{
return _node == it._node;
}
};
然后把他们const的类型的加入到List类里面
cpp
typedef ListConstIterator<T> const_iterator;
const_iterator begin() const
{
// iterator it(_head->_next);
// return it;
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}





然后我们发现const_iterator类和iterator类只有返回类型不同
我们可以通过控制模板参数来进行实现代码的复用

代码如下:
cpp
template<class T>
struct ListNode
{
ListNode<T>* _next;
ListNode<T>* _prev;
T _data;
ListNode(const T& data = T())
:_next(nullptr)
,_prev(nullptr)
,_data(data)
{
}
};
template<class T,class Ref,class Ptr>
struct ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T,Ref,Ptr> Self;
Node* _node;
ListIterator(Node* node)
:_node(node)
{
}
// ++it
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
Self& operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
Ptr operator->()
{
return &_node->_data;
}
Ref operator*()
{
return _node->_data;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
bool operator==(const Self& it)
{
return _node == it._node;
}
};
template<class T>
class List
{
typedef ListNode<T> Node;
public:
// typedef ListIterator<T> iterator;
// typedef ListConstIterator<T> const_iterator;
typedef ListIterator<T,T&,T*> iterator;
typedef ListIterator<T,const T&,const T*> const_iterator;
const_iterator begin() const
{
// iterator it(_head->_next);
// return it;
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
iterator begin()
{
// iterator it(_head->_next);
// return it;
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
List()
{
head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
void push_back(const T& x)
{
Node* newnode = new Node(x);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
private:
Node* _head;
};

五.insert(),erase(),pop_back()和pop_front()的实现
1.insert()实现:
cpp
void insert(iterator pos,const T& x)
{
Node* cur = pos._node;
Node* newnode = new Node(x);
Node* prev = cur->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
链表的迭代器,不会由迭代器失效,因为没有扩容的概念,但是库里面有返回值我们还是把返回值带上

cpp
iterator insert(iterator pos,const T& x)
{
Node* cur = pos._node;
Node* newnode = new Node(x);
Node* prev = cur->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
return iterator(newnode);
}

2.erase()实现:
cpp
//erase后 pos失效,因为当前节点被删除了
void erase(iterator pos)
{
Node* cur = pos._node;
Node* newnode = new Node(x);
Node* prev = cur->_prev;
prev->_next = next;
next->_prev = prev;
delete cur;
}
erase有迭代器失效,我们要返回删除元素的后面一个元素

cpp
//erase后 pos失效,因为当前节点被删除了
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* newnode = new Node(x);
Node* prev = cur->_prev;
prev->_next = next;
next->_prev = prev;
delete cur;
return iterator(next);
}
3.pop_back()实现:
cpp
void pop_back()
{
erase(--end());
}
我们可以直接复用
4.pop_front()实现:
cpp
void pop_front()
{
erase(begin());
}
5.push_front()的实现
cpp
void push_front(const T& x)
{
insert(begin(),x);
}
测试代码:
cpp
void test_list4()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
lt1.push_back(5);
Func(lt1);
lt1.push_front(10);
lt1.push_front(20);
lt1.push_front(30);
Func(lt1);
lt1.pop_front();
lt1.pop_front();
Func(lt1);
lt1.pop_back();
lt1.pop_back();
Func(lt1);
lt1.pop_back();
lt1.pop_back();
lt1.pop_back();
lt1.pop_back();
//lt1.pop_back();
Func(lt1);
}
六.list的拷贝
我们的迭代器想要的是浅拷贝,但是我们的list不能浅拷贝啊


1.析构函数的补充(浅拷贝会析构两次)
一般我们链表都是会实现clear()的,所以我们先实现clear(),然后我们在写析构函数的时候,进行复用
1.clear()
cpp
void clear()
{
auto it = begin();
while(it != end())
{
it = erase(it);
}
}
2.~list()
cpp
~List()
{
clear();
delete _head;
_head = nullptr;
}
3.重新默认构造
cpp
void empty_init()
{
head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
List()
{
empty_init();
}
将哨兵位的头节点单拎出来,变成一个head
4.拷贝构造
cpp
//lt2(lt1)
list(const list<T>& lt)
{
empty_init();
for(auto e : lt)
{
push_back(e);
}
}
我们这里最好加上 const 和 & ,避免拷贝(这里的引用,是引用的lt里面的值)
cpp
//lt2(lt1)
list(const list<T>& lt)
{
empty_init();
for(const auto& e : lt)
{
push_back(e);
}
}

2.operator=的重载
cpp
list<T>& operator=(list<T> lt)
{
swap(_head,lt._head);
return *this;
}
3.initializer_list的构造
cpp
//这里是两个指针,所以我们可以不传&
list(initializer_list<T> il)
{
empty_init();
for(const auto& e:il)
{
push_back(e);
}
}

vs的这种写法会更好,但是我们还是要代码写得规范