前言
stl中list中list的迭代器跟vector有所不同,其中有一些难懂的地方,来帮助大家稍微理解一下
这是源码中实现的方式,难以理解的地方就是在于这三个模板参数
我们先仿照源码写一个简化版的迭代器,接下来我们将逐一讨论剩下的两个模板参数的作用
#include<iostream>
using namespace std;
namespace wzy
{
//节点
template<class T>
struct list_node
{
list_node<T>* _prev;
list_node<T>* _next;
T _data;
//构造函数
list_node(const T& x=T())
:_next(nullptr)
,_prev(nullptr)
,_data(x)
{}
};
//迭代器
template<class T>
struct __list_iterator
{
typedef __list_iterator<T> self;
typedef list_node<T> node;
node* _node;
__list_iterator(node* n)
:_node(n)
{}
//不同于vector的原生迭代器,list由于空间不连续,所以要重载一下++
//而++就是让指针移动到下个节点
self& operator++()
{
_node = _node->_next;
return *this;
}
T& operator*()
{
return _node->_data;
}
bool operator!=(const self& x)
{
return _node != x._node;
}
};
//list
template<class T>
class list
{
public:
typedef list_node<T> node;
typedef __list_iterator<T> iterator;
iterator begin()
{
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* new_node = new node(x);
node* tail = _head->_prev;
tail->_next = new_node;
new_node->_prev = tail;
_head->_prev = new_node;
new_node->_next = _head;
}
private:
node* _head;
};
void test()
{
list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
list<int>::iterator it = l.begin();
while (it != l.end())
{
cout << *it << " ";
++it;
}
cout << endl;
print(l);
}
}
一、增加第二个模板参数的原因
我们经常会遇到要使用const迭代器的场景,比如这样一个函数
此时我们要想遍历链表中的数据就只能使用const迭代器,毫无疑问再向上面一样去使用普通的迭代器已经不可以了,因为const对象无法调用非const成员函数
void print(const list<int>& ll)
{
list<int>::iterator it = ll.begin();
while (it != ll.end())
{
cout << *it << " ";
++it;
}
}
此时我们一个想法就是直接在begin()函数后面加上const构成重载不就可以了么,可以么?我们来看
iterator begin() const
{
return iterator(_head->_next);
}
iterator end() const
{
return iterator(_head);
}
运行结果没错,也能遍历成功,但是我们在print()加上这样一句代码
void print(const list<int>& ll)
{
list<int>::iterator it = ll.begin();
while (it != ll.end())
{
(*it)++;
cout << *it << " ";
++it;
}
}
此时我们发现结果已经不符合我们预期了,因为按理来说,const迭代器是不允许对内容进行修改的,说明我们某个地方出错了
究其原因是因为我们还是用const对象去构造出了一个普通的迭代器,为什么会这样呢,是因为这里const修饰的是*this *this就是 _head这个指针,而指针(_head)不能被修改,不意味着不能被拷贝给其他。举个例子来说
这样可以么,也不可以,这里的 iterator 就相当于 T * 而这里的加上const之后,相当于T * const ,我们需要的是指向的内容不被改变,而不是指针本身不可改变,所以也是错误的
typedef __list_iterator<T> iterator;
typedef const iterator const_iterator;
正确的写法是,实现一个const版本迭代器
template<class T>
struct __list_const_iterator
{
typedef __list_const_iterator<T> self;
typedef list_node<T> node;
node* _node;
__list_const_iterator(node* n)
:_node(n)
{}
self& operator++()
{
_node = _node->_next;
return *this;
}
const T& operator*()
{
return _node->_data;
}
bool operator!=(const self& x)
{
return _node != x._node;
}
};
然后修改list部分
此时我们的运行结果就是正确的了
但是我们不难发现__list_const_iterator类和__list_iterator类在实现上几乎是一模一样的,就是const T& operator*() 返回值类型不一样,为了优化这一结果我们增加了第二个模板参数
template<class T, class Ref>
struct __list_iterator
{
typedef __list_iterator<T, Ref> self;
typedef list_node<T> node;
node* _node;
__list_iterator(node* n)
:_node(n)
{}
self& operator++()
{
_node = _node->_next;
return *this;
}
Ref operator*()
{
return _node->_data;
}
bool operator!=(const self& x)
{
return _node != x._node;
}
};
并修改
二、增加第三个模板参数的原因
在介绍第三个模板参数之前,我们先来回忆一下什么情况下需要 ->
struct A
{
int _a1;
int _a2;
A(int a1=1, int a2=1)
:_a1(a1)
, _a2(a2)
{}
};
void test2()
{
list<A> l;
l.push_back(A(1, 2));
l.push_back(A(3, 4));
l.push_back(A(5, 6));
list<A>::iterator it = l.begin();
while (it != l.end())
{
cout << *it<<" ";
++it;
}
}
这时*it就无法直接获取成员数据,对于自定义类型来说只能这样去做
cout << (*it)._a1 << " " << (*it)._a2<<" ";
这时就需要 ->发挥作用了
cout<<it->_a1<<" "<<it->_a2<<" ";
所以我们需要重载operator ->
T* operator->()
{
return &_node->_data;
}
这段代码的含义:_node->data 就是list存储的节点的类型 在这里就是自定义类型A,然后&(取地址)就是获得了指向A类型的指针,那么自定义类型指针再去获取成员变量就可以 使用->了
根据上面的描述应该是 it-> ->_a1才对,因为第一个是获取指针,第二个才是获取成员变量
这里只有一个的原因是因为编译器为了增强可读性 做了优化省略了一个 ->
那么增加第三个模板参数的原因就显而易见了,跟第二个相同,就是为了解决const这一版本。
完整的代码:
namespace wzy
{
struct A
{
int _a1;
int _a2;
A(int a1 = 1, int a2 = 1)
:_a1(a1)
, _a2(a2)
{}
};
template<class T>
struct list_node
{
list_node<T>* _prev;
list_node<T>* _next;
T _data;
list_node(const T& x = T())
:_next(nullptr)
, _prev(nullptr)
, _data(x)
{}
};
template<class T, class Ref, class Ptr>
struct __list_iterator
{
typedef __list_iterator<T, Ref, Ptr> self;
typedef list_node<T> node;
node* _node;
__list_iterator(node* n)
:_node(n)
{}
self& operator++()
{
_node = _node->_next;
return *this;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const self& x)
{
return _node != x._node;
}
};
template<class T>
class list
{
public:
typedef list_node<T> node;
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
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);
}
list()
{
_head = new node();
_head->_next = _head;
_head->_prev = _head;
}
void push_back(const T& x)
{
node* new_node = new node(x);
node* tail = _head->_prev;
tail->_next = new_node;
new_node->_prev = tail;
_head->_prev = new_node;
new_node->_next = _head;
}
private:
node* _head;
};
void print(const list<int>& ll)
{
list<int>::const_iterator it = ll.begin();
while (it != ll.end())
{
//(*it)++;
cout << *it << " ";
++it;
}
}
void test()
{
list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
list<int>::iterator it = l.begin();
const type_info& objInfo = typeid(l.begin());
cout << objInfo.name() << endl;
while (it != l.end())
{
cout << *it << " ";
++it;
}
cout << endl;
print(l);
}
void print(const list<A>& ll)
{
list<A>::const_iterator it = ll.begin();
while (it != ll.end())
{
//(*it)++;
cout << it->_a1 << " " << it->_a2 << " ";
++it;
}
}
void test2()
{
list<A> l;
l.push_back(A(1, 2));
l.push_back(A(3, 4));
l.push_back(A(5, 6));
list<A>::iterator it = l.begin();
while (it != l.end())
{
cout << it->_a1 << " " << it->_a2 << " ";
++it;
}
cout << endl;
print(l);
}
}