1. 前言
list
是C++ STL
的一个容器,它的底层是双向循环链表
本篇文章的重点:
list
的iterator
实现逻辑- 利用模板实现
iterator
和const_iterator
2. 模拟实现
由于list
的底层是双向循环链表,因此我们得有节点,并且链表最开始是有一个哨兵位的头节点的
C++
namespace byh
{
template<class T>
struct ListNode
{
ListNode(const T& val = T())
:_next(nullptr)
,_prev(nullptr)
, _val(val)
{}
ListNode<T>* _next;
ListNode<T>* _prev;
T _val;
};
template<class T>
class list
{
typedef ListNode<T> Node;
public:
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
void push_back(const T& val)
{
Node* newNode = new Node(val);
Node* tail = _head->_prev;
tail->_next = newNode;
newNode->_prev = tail;
newNode->_next = _head;
_head->_prev = newNode;
_size++;
}
private:
Node* _head;
size_t _size;
};
}
2.1 iterator
的设计
尾插数据后,我们想遍历数据,使用for
循环不能遍历,因为链表的数据在内存中并不连续;范围for
的底层则是迭代器
之前的string
和vector
的迭代器我们是拿原生指针实现的,一方面指针指向的内容就是我们要遍历的数据,另一方面数据在内存中是连续的,指针++
就是往后走
但list
如果拿原生指针实现,存在下面的问题:
- 解引用指针得到的是一个节点,而我们要的是节点中的值
list
的节点在内存中不连续,指针++
走到的不一定是下一个节点
迭代器的目的是不需要我们去考虑底层,所有容器遍历方式都是一样的
我们希望解引用和++
按照我们指定的方式来操作节点,而C++中支持运算符重载,于是能不能将迭代器设计成一个类,在类中,我们自主定义*
和++
,再由list
提供begin()
和end()
接口
C++
template<class T>
struct ListIterator
{
typedef ListNode<T> Node;
ListIterator(Node* node)
:_node(node)
{}
T& operator*()
{
return _node->_val;
}
// ++it
ListIterator& operator++()
{
_node = _node->_next;
return *this;
}
// it++
ListIterator operator++(int)
{
ListIterator temp = *this;
_node = _node->_next;
return temp;
}
// --it
ListIterator& operator--()
{
_node = _node->_prev;
return *this;
}
// it--
ListIterator operator--(int)
{
ListIterator temp = *this;
_node = _node->_prev;
return temp;
}
bool operator!=(const ListIterator& li)
{
return _node != li._node;
}
bool operator==(const ListIterator& li)
{
return _node == li._node;
}
Node* _node;
};
// list中定义
iterator begin()
{
return _head->_next;
}
iterator end()
{
return _head;
}
实际上,list
的迭代器其本质还是一个指针,只不过我们对该指针进行封装,并根据语法规则自主定义了该指针的行为
迭代器模拟的就是指针的行为
如果list
中的数据类型是一个自定义类型,解引用迭代器得到一个结构体,用.
操作符,这点跟C语言相同,但更多的时候我们用结构体的指针加上->
来访问结构体中的数据;因此,就需要我们对->
进行重载
C++
template<class T>
struct ListIterator
{
//...
T* operator->()
{
return &this->_node->_val;
}
//...
}
struct A
{
A(int a1 = 0, int a2 = 0)
:_a1(a1)
,_a2(a2)
{}
int _a1;
int _a2;
};
void Test_list()
{
list<A> li;
li.push_back({ 10,10 });
list<A>::iterator it = li.begin();
cout << (*it)._a1 << " " << (*it)._a2 << endl;
cout << it->_a1 << " " << it->_a1 << endl;
// 实际上是 it.operator->()->_a1
// 为了可读性,编译器简化成一个->
}
2.2 const_iterator
的设计
C++
void Print_list(const list<int>& cli)
{
typename list<int>::iterator it = cli.begin();
while (it != cli.end())
{
cout << *it << " ";
it++
}
cout << endl;
}
void Test_list()
{
list<int> li;
li.push_back(1);
li.push_back(2);
li.push_back(3);
li.push_back(4);
Print_list(li);
}
上面代码执行,编译器会报错,原因是cli
是const
对象,而begin
是非const
成员函数,const
对象不能调用非const
成员函数
那给begin()
成员函数加上const
修饰不就行了?这样做代码确实能跑,但问题是,cli
是const
对象,内容不能被修改,而我们现在能随意修改cli
正确的做法是重新设计一个const_iterator
,给const
对象使用
- 将
ListIterator
拷贝一份,修改成ConstListIterator
- 缺点:代码冗余
- 利用模板特性,将
Iterator
改成模板
C++
template<class T, class Ref, class Ptr>
struct ListIterator
{
//...
Ref operator*()
{
return _node->_val;
}
Ptr operator->()
{
return &this->_node->_val;
}
//...
};
template<class T>
class list
{
//...
public:
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;
//...
}
3. 完整代码
C++
namespace byh
{
template<class T>
struct ListNode
{
ListNode(const T& val = T())
:_next(nullptr)
,_prev(nullptr)
,_val(val)
{}
ListNode<T>* _next;
ListNode<T>* _prev;
T _val;
};
template<class T, class Ref, class Ptr>
struct ListIterator
{
typedef ListNode<T> Node;
ListIterator(Node* node)
:_node(node)
{}
Ref operator*()
{
return _node->_val;
}
Ptr operator->()
{
return &this->_node->_val;
}
// ++it
ListIterator& operator++()
{
_node = _node->_next;
return *this;
}
// it++
ListIterator operator++(int)
{
ListIterator temp = *this;
_node = _node->_next;
return temp;
}
// --it
ListIterator& operator--()
{
_node = _node->_prev;
return *this;
}
// it--
ListIterator operator--(int)
{
ListIterator temp = *this;
_node = _node->_prev;
return temp;
}
bool operator!=(const ListIterator& li)
{
return _node != li._node;
}
bool operator==(const ListIterator& li)
{
return _node == li._node;
}
Node* _node;
};
template<class T>
class list
{
typedef ListNode<T> Node;
public:
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
empty_init();
}
list(const list<T>& li)
{
empty_init();
for (auto& e : li)
{
push_back(e);
}
}
list<T>& operator=(list<T> li)
{
swap(li);
return *this;
}
void swap(list<T> li)
{
std::swap(_head, li._head);
std::swap(_size, li._size);
}
iterator begin()
{
return _head->_next;
}
iterator end()
{
return _head;
}
const_iterator begin() const
{
return _head->_next;
}
const_iterator end() const
{
return _head;
}
void push_back(const T& val)
{
insert(end(), val);
}
void push_front(const T& val)
{
insert(begin(), val);
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
void insert(iterator pos, const T& val)
{
Node* newNode = new Node(val);
Node* prev = pos._node->_prev;
prev->_next = newNode;
newNode->_prev = prev;
newNode->_next = pos._node;
pos._node->_prev = newNode;
_size++;
}
iterator erase(iterator pos)
{
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
_size--;
return next;
}
bool empty() const
{
return _size == 0;
}
size_t size() const
{
return _size;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
// 析构
~list()
{
clear();
delete _head;
_head = nullptr;
_size = 0;
}
private:
Node* _head;
size_t _size;
};
}