【C++】C++ STL 探索:List使用与背后底层逻辑(一)https://developer.aliyun.com/article/1617347
2.8 const_Iterator迭代器
2.8.1 实现const_Iterator迭代器
关于迭代器相关接口已经实现完毕,如果我们需要实现个指向内容不可以被修改的迭代器呢?
思考问题:
- 我们为什么要用const_Iterator而不是const Iterator呢?
- const修饰迭代器,是迭代器本身不能被修改,还是迭代器指向内容不能被修改呢?
const Iterator begin() const { return _head->_next; } const Iterator end() const { return _head; }
我们实现const版本迭代器目的就是为了指向内容不被修改。
分析过程:
- Iterator是一个类,如果在前面加const,它表示的意思是这个类对象本身不能被修改,而不是指向内容不能被修改。
- 实现链表的迭代器不是内置类型,而是自定义类型,在自定义类型前面使用const修饰代表本身不能被修改,会导致++或–运算符之类的运算符重载会失效。
- 指出问题:迭代器指向内容是否被修改,需要对实现迭代器的类里面解引用运算符重载进行const修饰
template<class T> struct ListConIterator { typedef ListNode<T> Node; typedef ListConIterator<T> Self; //只针对解引用 修改了迭代器的行为 但是++ --是将迭代器位置改变,迭代器指向的内容不能被修改 Node* _node; //迭代器还是依赖指针,通过类来改变运算符的行为 //这里是浅拷贝 拷贝构造函数 ListIterator(Node* node) :_node(node) {} Self& operator++() { _node = _node->_next; return *this; } //后缀++ Self operator++(int) { //构造一个tmp临时变量 Self tmp(*this); _node = _node->_next; return tmp; } //这里需要判断空 Self& operator--() { _node = _node->_prev; return *this; } // //后缀-- Self operator--(int) { //构造一个tmp临时变量 Self tmp(*this); _node = _node->_prev; return tmp; } 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 T* operator->() { return &_head->_data; } };
具体说明:将解引用操作符重载的返回值用const修饰,保证返回的数据不能被修改。这里没有对++和–的返回值进行修饰,因为++和–并不直接影响迭代器的内容,是针对迭代器(it)本身。
2.8.2 模板整合类型问题
不足之处:对于Ierator类与const_Ierator类的解引用运算符重载大体功能相同,主要区别在于不同情况下需要使用返回值类型不同,实现两个类显得有点冗余。如果问题是在于类型上差异导致的,可以将两个封装到同一个类中,使用模板实现。
typedef ListIterator<T, T&, T*> Iterator; typedef ListIterator<T, const T&, const T*> const_Iterator; 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) {} //Self& operator++() Self& operator++() { _node = _node->_next; return *this; } //后缀++ Self operator++(int) { //构造一个tmp临时变量 Self tmp(*this); _node = _node->_next; return tmp; } Self& operator--() { _node = _node->_prev; return *this; } //后缀-- Self operator--(int) { //构造一个tmp临时变量 Self tmp(*this); _node = _node->_prev; return tmp; } // T& operator* () Ref operator* () { return _node->_data; } //通过指针指向的节点去判断是否相等 bool operator==(const Self& it) { return _node == it._node; } bool operator!=(const Self& it) { return _node != it._node; } //T* operator->() Ptr operator->() { return &_head->_data; } };
2.9 打印链表元素
void PrintList(const list<int>& clt) { list<int> ::Iterator it = clt.begin(); while (it != clt.end()) { *it += 10; cout << *it << " "; ++it; } cout << endl; }
三、常规接口实现
3.1 构造函数
void empty_init() { _head = new Node; _head->_next = _head; _head->_prev = _head; _size = 0; }
构造和拷贝构造都需要一份节点,那么可以复用一份
这里不需要对_data进行初始化,在new Node时已经完成
list() { empty_init(); }
3.2 拷贝构造
list(const list<T>& lt) { empty_init(); for (auto e : lt) { push_back(e); } }
3.3 operator= 赋值运算符
list<T>& operator=(list<T> it) { swap(it); return *this; }
3.4 clear 清除
void clear() { Iterator it = begin(); while (it != end()) { it = erase(it); } }
说明:
- 这里是简单的资源清除,没有删除哨兵位
- 同时在使用erase时,需要将下一个位置迭代器返回
3.5 ~list 析构函数
~list() { clear(); delete _head; _head = nullptr; }
3.6 size 查看当前链表元素个数
//获得基本信息 size_t size() { return _size; }
3.7 empty 判空
bool empty() { return _size == 0; }
【C++】C++ STL 探索:List使用与背后底层逻辑(三)https://developer.aliyun.com/article/1617350