list 的模拟实现中,重难点在于迭代器功能的实现,因此本文只围绕
iterator
及const_iterator
的设计进行介绍,其余如增删查改则不再赘述——在C语言的基础上,这些都非常简单。与 string / vector 不同,list 的节点原生指针不能通过简单的 ++ / * 等实现迭代器,因此我们需要对
节点指针
进行封装
,利用自定义类型
支持运算符重载
的性质,完成迭代器的设计。为了与 STL中list 进行区分,我们在创建的命名空间内实现。
一、iterator 的实现
(双向带头循环)链表及其节点的结构设计:
push_back():
void push_back(const T& x)
{Node* newnode = new Node(x);Node* tail = _head->prev;tail->_next = newnode;newnode->_prev = tail;_head->_prev = newnode;newnode->_next = _head;
}
我们很容易可以插入一组数据,但如果想遍历链表,同时对数据进行修改,则需要实现迭代器。
list 无法像 vector 一样简单地 ++/* 实现节点遍历,且原始指针也不支持对运算符重载,我们需要对 list节点的指针 进行封装。
template<class T>
struct __list_iterator
{typedef ListNode<T> Node;Node* _node;// 被封装的节点指针// 迭代器的构造__list_iterator(Node* node):_node(node){}
};
struct __list_iterator
{typedef __list_iterator<T> self; self operator++(){_node = _node->_next;return _node;}T& operator*(){return _node->_data;}
};
实际上,编译器优化后,会直接用 _node 对等式左边的变量进行构造。
以此类推,后置++ / 前后置-- / != / == 等都很容易了。
二、const_iterator 及第二个模板参数的引入
const_iterator 不能通过对 iterator 加 const 实现:
const_iterator 是为了防止 list 节点的数据被修改;iterator 加 const 会让迭代器本身无法++/–,导致 const list 无法实现遍历——迭代器不能++/–
const_iterator 本身的功能很简单——防止 list 节点的数据被修改,我们只需要针对性的修改 * 重载
,其余与普通迭代器没有区别。
const T& operator*()
{return _node->_data;
}
// 普通迭代器 iterator:
// T& operator*() —— 能对节点的数据进行修改
即:
template<class T> struct __list_const_iterator {// ...const T& operator*(){return _node->_data;} };template<class T> class list {// ...typedef __list_const_iterator<T> const_iterator; }
仅有一个函数不同,而其余部分都一样(与 iterator 相比),这种做法不是很好。
因此,我们引入第二个模板参数。
template<class T, class Ref>
struct __list_iterator
{// ...Ref operator*(){return _node->_data;}
}template<class T, class Ref>
class list
{typedef __list_iterator<T, T&> iterator;typedef __list_iterator<T, const T&> const_iterator;// ...
}
- 用一个例子测试 const迭代器:
namespace MyList {void Print(const list<int>& lt){for (auto& e : lt){cout << e << " ";}cout << endl;}void test1(){list<int> lt;lt.push_back(10);lt.push_back(20);lt.push_back(30);lt.push_back(40);Print(lt);} }int main() {test1();return 0; }
三、箭头->
重载
T* operator->()
{return &(_node->_data);
}
观察一下:it->_year ——>it.operator->()_year
此处 it.operator->() 的返回值是 Date* ,不应该写成
it->->_year
吗?事实上,编译器在此处进行了优化,->-> 的写法反而错了
我们也可以引入第三个模板参数 Ptr
,这就就可以同时实现 T* 和 const T* 。
template<class T, class Ref, class Ptr>
struct __list_iterator
{typedef __list_iterator<T, Ref, Ptr> self;// ...
};template<class T>
class list
{typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;// ...
}