核心框架:
template <class T>
struct __list_node {
typedef void* void_pointer; // 结点指针
void_pointer next;
void_pointer prev;
T data;
};
template <class T, class Alloc = alloc>
class list {
protected:
typedef void* void_pointer;
typedef __list_node<T> list_node;
typedef list_node* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
public:
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
protected:
link_type node;
};
namespace zmm
{
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->_prev = _head;
_head->_next = _head; // 因为是双向循环链表
}
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;
}
private:
Node* _head;
};
void test_list1()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
}
}
在list中,我们如何去实现迭代器?此时it++就不是指向下一个位置了,因为链表每个结点可能存在的物理地址都不一样,这样子++就不对了,但是C++中提供了运算符重载。
__list_iterator<T>& operator++()
{
_node = _node->_next;
return *this;
}
// 后置++,返回++之前的值
__list_iterator<T>& operator++(int)
{
__list_iterator<T> tmp(*this);
_node = _node->_next;
return tmp;
}
bool operator!=(const __list_iterator<T>& it)
{
return _node != it._node;
}
因此综上,最初的基础框架就完成了
namespace zmm
{
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>
struct __list_iterator
{
typedef ListNode<T> Node;
// typedef __list_iterator
Node* _node; // 构造出了结点
__list_iterator(Node* x) // 用结点的指针去构造迭代器
:_node(x)
{}
T& operator*() // 可读可写
{
return _node->_data;
}
// 前置++,返回++之后的值
__list_iterator<T>& operator++()
{
_node = _node->_next;
return *this;
}
// 后置++,返回++之前的值
__list_iterator<T>& operator++(int)
{
__list_iterator<T> tmp(*this);
_node = _node->_next;
return tmp;
}
bool operator!=(const __list_iterator<T>& it)
{
return _node != it._node;
}
};
template <class T>
class list
{
typedef ListNode<T> Node;
public:
typedef __list_iterator<T> iterator;
// begin end都是用结点的指针去构造的迭代器
iterator begin()
{
// head->next就是第一个数据
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
list()
{
_head = new Node;
_head->_prev = _head;
_head->_next = _head; // 因为是双向循环链表
}
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;
}
private:
Node* _head;
};
void test_list1()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
}
list模拟实现需要去实现拷贝构造、赋值重载(深拷贝)以及析构函数吗?
——不需要,使用默认生成的即可
list
比如这句,就是浅拷贝,我们就是要让他们指向同一个位置,就是让节点的地址给it
析构也不需要去实现——迭代器是借助结点的指针去访问修改链表,结点属于链表,不属于迭代器,所以他不管释放,都不需要自己去实现
const迭代器
void print_list(const list<int>& lt) // 必须要加引用,否则就是浅拷贝
{
// 这里是没办法遍历的,lt是const对象,需要使用const迭代器
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
const迭代器与普通迭代器的区别:普通迭代器访问普通对象可读可写,而const只能读,那我们该如何去实现?
可以这样做吗?——不行
T& operator*() const
{
return _node->_data;
}
const T& operator*()
{
return _node->_data;
}
因为我们是容器为const,*it是迭代器去调用的,但迭代器还是原先的iterator,而非是const_iterator
所以还需要在实现一个const迭代器
template <class T>
struct __list_iterator
{
typedef ListNode<T> Node;
// typedef __list_iterator
Node* _node; // 构造出了结点
__list_iterator(Node* x) // 用结点的指针去构造迭代器
:_node(x)
{}
__list_iterator<T>& operator++(int)
{
__list_iterator<T> tmp(*this);
_node = _node->_next;
return tmp;
}
// ...
};
template <class T>
struct __const_list_iterator
{
typedef ListNode<T> Node;
// typedef __list_iterator
Node* _node; // 构造出了结点
__const_list_iterator(Node* x) // 用结点的指针去构造迭代器
:_node(x)
{}
const T& operator*() const
{
return _node->_data;
}
__const_list_iterator<T>& operator++()
{
_node = _node->_next;
return *this;
}
};
这样做我们就实现了一个普通的iterator和const_iterator迭代器,但是这样就有代码冗余了,所以还应该进一步的修改
template
增加了一个模板参数ref
之后修改为这个
// T& operator*() 原先版本
ref operator*()
{
return _node->_data;
}
在对class list进行修改
class list
{
typedef __list_iterator<T, T&> iterator;
typedef __list_iterator<T, const T&> const_iterator;
};
又加入了一个模板参数后,这样子就可以防止代码冗余
但是这里就算是const,也可以去用->去修改对象,所以我们要在传入一个参数Ptr
因此最终的框架就形成了:
template <class T, class Ref, class Ptr>
struct __list_iterator
{
typedef ListNode<T> Node;
// 由于类模板添加了一些新的参数,为了方便些,再次typedef
typedef __list_iterator<T, Ref, Ptr> self;
// typedef __list_iterator
Node* _node; // 构造出了结点
__list_iterator(Node* x) // 用结点的指针去构造迭代器
:_node(x)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
};
template <class T>
class list
{
typedef ListNode<T> Node;
public:
typedef __list_iterator<T, T&, T*> iterator; // 普通迭代器返回T*
typedef __list_iterator<T, const T&, const T*> const_iterator; // // const迭代器返回T*
private:
Node* _head;
};
vector和list的区别
vector缺点:
连续的物理空间,是优势,也是一种劣势
优势:支持高效随机访问
劣势:
1、空间不够要增容, 增容代价比较大
2、可能存在一定的空间浪费,按需申请,会导致频繁的增容,所以一般会增容二倍左右(一般也不会造成每次都要增容)
3、头部或者中部插入删除需要挪动数据,效率低下(最致命的缺陷)
因此vector适合尾插尾删,随机访问
list很好地解决了vector的以上问题
1、list任意位置支持O(1)的插入删除(双向链表)
2、按需申请释放空间
因此list适合任意位置插入删除
本质上list和vector是两个互补的数据结构,在实际中,vector用的更多一些
STL中的所有容器都不保证线程安全
现在所提及的还只是链式结构,后面还有二叉树结构,哈希结构等
template <class T>
class list
{
typedef ListNode<T> Node;
public:
typedef __list_iterator<T, T&, T*> iterator; // 普通迭代器返回T*
typedef __list_iterator<T, const T&, const T*> const_iterator; // // const迭代器返回T*
private:
Node* _head;
};
// 这里insert后,pos会失效吗?——不会,插入数据后,pos位置没变
// pos位置前插入数据
iterator insert(iterator pos, const T& x) // 迭代器是一个对象,用.访问成员
{
// 返回值指向新插入的元素
Node* cur = pos._node; // 用迭代器拿到这个结点
Node* prev = cur->_prev;
Node* newNode = new Node(x);
prev->_next = newNode;
newNode->_prev = prev;
newNode->_next = cur;
cur->_prev = newNode;
return iterator(newNode);
}
// vector失效原因有两点,一点是扩容会导致失效,第二点是插入了以后,pos指向的值就变了
因此insert是不会有迭代器失效的问题
// erase之后pos会失效吗——会失效,pos指向的节点就没了
iterator erase(iterator pos)
{
assert(pos != end()); // 不能erase哨兵位
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
delete pos._node;
prev->_next = next;
next->_prev = prev;
return iterator(next);
}
修改版:
void clear()
{
// 清除掉链表里面所有的数据(不删除哨兵位)
iterator it = begin();
while (it != end())
{
/*iterator del = it++; // 双向迭代器不支持it+1,随机迭代器才可以
delete del._node;*/
// 也可以去复用erase
iterator del = it++;
erase(del);
// 也可以直接用erase(it++)去替换上面两行
}
_head->_next = _head;
_head->_prev = _head;
}
传统写法
// lt2(lt1)
list(const list<T>& lt) // lt就是lt1
{
_head = new Node;
_head->_prev = _head;
_head->_next = _head;
// 传统写法:
for (auto e : lt)
{
// push_back前提是要初始化后才可以使用
push_back(e); // this的pushback
}
}
// lt2 = lt1
list<T>& operator=(const list<T>& lt)
{
if (this != <)
{
clear(); // 清除l2的所有数据也就是this
for (auto e : lt) // 遍历lt1
{
push_back(e);
}
}
return *this;
}
上面拷贝构造函数还是有问题,_head是随机值,与他交换后就会蹦,因此改为下面这种写法
list(const list<T>& lt)
{
_head = new Node();
_head->_prev = _head;
_head->_next = _head;
list<T> tmp(lt.begin(), lt.end());
std::swap(_head, tmp._head);
}
在添加一个构造函数
// 这里最好给个缺省值,因为val可能是日期类,vector等等
list(size_t n, const T& val = T()) // 用n个val去初始化和一个迭代器区间初始化可能存在冲突
{
_head = new Node();
_head->_prev = _head;
_head->_next = _head;
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
这里有两个构造函数,如果一个函数去调用他们,是去调用哪一个?——去调用类型最匹配的那一个
反向迭代器的源码在stl_iterator.h文件中
template <class Iterator>
class reverse_iterator
{
protected:
Iterator current;
public:
typedef typename iterator_traits<Iterator>::iterator_category
iterator_category;
typedef typename iterator_traits<Iterator>::value_type
value_type;
typedef typename iterator_traits<Iterator>::difference_type
difference_type;
typedef typename iterator_traits<Iterator>::pointer
pointer;
typedef typename iterator_traits<Iterator>::reference
reference;
typedef Iterator iterator_type;
typedef reverse_iterator<Iterator> self;
public:
reverse_iterator() {}
explicit reverse_iterator(iterator_type x) : current(x) {}
reverse_iterator(const self& x) : current(x.current) {}
#ifdef __STL_MEMBER_TEMPLATES
template <class Iter>
reverse_iterator(const reverse_iterator<Iter>& x) : current(x.current) {}
#endif /* __STL_MEMBER_TEMPLATES */
iterator_type base() const { return current; }
reference operator*() const {
Iterator tmp = current;
return *--tmp;
}
self& operator++() {
--current;
return *this;
}
self operator++(int) {
self tmp = *this;
--current;
return tmp;
}
self& operator--() {
++current;
return *this;
}
self operator--(int) {
self tmp = *this;
++current;
return tmp;
}
可以发现,反向迭代器里面是包含了正向迭代器,反向迭代器的++就是调用正向迭代器的–,因此反向迭代器实际上就是整形迭代器的一种封装
reference operator*() const {
Iterator tmp = current;
return *--tmp;
}
解引用是取的正向迭代器的前一个位置,为什么会这样子?
查看stl_list.h源码可以发现:
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
在传统上,我们定义rend和rbegin是这样的:
但是在源码中,定义的却不是这样子,而是下面的情况
这样设计的意义就是:
正向迭代器的开始就是反向迭代器的结束
正向迭代器的结束就是反向迭代器的开始
reverse_iterator rit = rbegin();
while (rit != rend())
{
*rit;
++rit;
}
这样子rit到达rend的时候,就停止了
因此operator*取前一个位置,主要目的就是为了让反向迭代器的开始和结束与正向迭代器对称
反向迭代器与正向迭代器的区别就是++、--的方向是相反的,所以反向迭代器封装正向迭代器即可,控制重载++、--的方向
反向迭代器的完整实现:
#pragma once
namespace zmm
{
template <class Iterator, class Ref, class Ptr>
class reverse_iterator
{
typedef reverse_iterator<Iterator, Ref, Ptr> self;
public:
reverse_iterator(Iterator it)
:_it(it)
{}
// 反向迭代器的解引用为什么是返回前一个位置?
Ref operator*()
{
Iterator prev = _it;
return *--prev;
}
Ptr operator->()
{
return &operator*();
}
self& operator++()
{
--_it; // 反向迭代器的++是倒着走的
return *this;
}
self& operator--()
{
++_it;
return *this;
}
bool operator!=(const self& rit) const
{
return _it != rit._it;
}
private:
Iterator _it; // _it是一个正向迭代器,可以是任意容器的正向迭代器,是一个模板
};
}
适配器也叫作配接器
Iterator是哪个容器的迭代器,reverse_iterator就可以是配出哪个容器的反向迭代器——复用的体现
凡是要取模板参数的内嵌类型都是需要添加typename的
typedef reverse_iterator<Iterator, Ref, Ptr> self;
// 上面三个模板参数太多了,因此我们是可以去使用内嵌类型的
typedef typename Iterator::Ref Ref // 告诉编译器Iterator::Ref是一个类型,等类模板实例化之后在取
typedef typename Iterator::Ptr Ptr
加typename的场景:
vector<int>::iterator it; // 此时类模板已经实例化了,是int类型,所以不需要加typename
list<int>::iterator it;
typename vector<T>::iterator it; // 等类模板实例化之后再去取iterator
typename list<T>::iterator it;