【C++】list及其模拟实现
本文是小编巩固自身而作,如有错误,欢迎指出!
一.list简介
1.list特点
C++ 中的 std::list
是标准模板库(STL)提供的一个容器,是一个双向带头循环链表。
2.list的常见用法
list与string和vector的常见接口很类似,但唯独有一个不太一样,就是排序接口sort,像string和vector都是以顺序表的方式实现,而string以链表的方式实现,导致其排序的底层实现与string和vector不同,在排序时更加复杂,导致耗费时间更多,因此,如果在元素个数非常多的情况下,我们一般可以将list的元素先拷贝到vector中后,进行排序
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>int main() {std::list<int> myList = {4,2,1,5,6};// 将元素从list拷贝到vectorstd::vector<int> myVector(myList.begin(), myList.end());// 在vector中排序std::sort(myVector.begin(), myVector.end());// 将排序后的元素从vector拷贝回listmyList.assign(myVector.begin(), myVector.end());for (auto e : myList) {std::cout << e << " ";}std::cout << std::endl;return 0;
}
二.list一些常见接口的模拟实现
1.类list的框架
我们回想一下,一个双向带头链表由什么组成?其就是由很多个节点组成,那么我们首先就能确定需要两个类,一个类是list这个链表,一个类是每一个节点
namespace yiming
{
template<class T>struct list_node//当不考虑用访问限定符做限制,所有成员公有的情况下,可以使用struct{T _data;list_node<T>* _next;list_node<T>* _prev;};
template<class T>class list{typedef list_node<T> Node;public:Node* _head;};
}
而我们要对链表进行操作,毫无疑问,我们需要用到指针,也就是迭代器,这里我们创建第三个类,迭代器
struct list_iterator
{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;Node* _node;
};
至于为什么这个模版有三个参数,我们后续详细讲
2.构造与析构
上面我们已经看了上述三个类的成员
我们现在看看其构造是怎么实现的
list_node(const T& x = T()):_data(x), _next(nullptr), _prev(nullptr){}
list_iterator(Node* node):_node(node){}
list(){_head = new Node;_head->_next = _head;_head->_prev = _head;}
~list(){clear();delete _head;_head = nullptr;}
void clear(){iterator it = begin();while (it != end()){it = erase(it);}}
大家可以看到,为什么只写了list的析构呢?其原因在于我们对链表操作时,使用迭代器,节点,希望其创造后仍可使用,而不是销毁,故其不需要析构函数。而我们后续对链表的操作,都是在list类的实现的,我们现在看看list类的其他构造函数
void empty_init()//初始化节点{_head = new Node;_head->_next = _head;_head->_prev = _head;}list(){empty_init();}list(const list<T>& lt){//先让新创建的对象拥有自己的头尾节点empty_init();//遍历将后续节点接入for (const auto& e : lt){push_back(e);}}list(initializer_list<T> il)//实现花括号进行初始化{empty_init();for (const auto& e : il){push_back(e);}}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=( list<T> lt)//不使用引用,会调用拷贝构造,不用担心右侧值被修改{swap(lt);return *this;}
3.迭代器操作
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);}
上述是一些简单的头尾迭代器,而且应该大家也会发现我们上述的迭代器其模版有三个参数
typedef list_iterator<T, Ref, Ptr> Self;typedef list_iterator<T,T&,T*> iterator;typedef list_iterator<T,const T&,const T*> const_iterator;
其主要原因就是,首先我们在进行解引用的操作时,我们希望得到其拷贝的值,故其使用Ref(reference),而我们使用时,时常会遇到const不匹配的情况,如下述代码
void print(const list<T><)//如何实现指向的内容不能修该但是能对it进行操作?{typename list<T>::const_iterator it = lt.begin();//类模版未实例化,不能去类模版中找后面的东西,typename告诉编译器是类型而非静态成员变量while (it != lt.end()){cout << *it << " ";++it;}cout << endl;}
其lt内的节点需要能够遍历而不能对其内的内容修改,也就是我们不能直接使用const进行修饰,那样会导致迭代器无法修改,导致无法 遍历,那我们按照直接的方法只能够选择一个新的类,其中全都是const类型的如下
template<class T>struct list_const_iterator{typedef list_node<T> Node;Node* _node;list_const_iterator(Node* node):_node(node){}~list_const_iterator()//迭代器不需要析构,由链表负责{}const T& operator*(){return _node->_data;}list_const_iterator<T>& operator++(){_node = _node->_next;return *this;}list_const_iterator<T>& operator++(int){list_const_iterator<T> tmp(*this);_node = _node->_next;return tmp;}list_const_iterator<T>& operator--(){_node = _node->_prev;return *this;}list_const_iterator<T>& operator--(int){list_const_iterator<T> tmp(*this);_node = _node->_prev;return tmp;}bool operator==(const list_const_iterator<T>& it)const{return _node == it._node;}bool operator!=(const list_const_iterator<T>& it)const{return _node != it._node;}};
很明显这样就太麻烦了,所以我们选择直接将模版给予多个参数,直接模版化,那么后续的迭代器实现就可以不需要多写一遍了
Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}bool operator==(const Self& it)const{return _node == it._node;}bool operator!=(const Self& it)const{return _node != it._node;}};
4.增删插改操作
其所有的头插,尾插头删尾删都是基于,insert插入,erase删除完成的
void push_back(const T& x){//Node* newnode = new Node(x);//Node* tail = _head->_prev;//tail->_next = newnode;//newnode->_prev = tail;//newnode->_next = _head;//_head->_prev = newnode;insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}iterator insert(iterator pos, const T& val){Node* cur = pos._node;Node* prev = cur->_prev; // 保存原始前驱节点Node* newnode = new Node(val);// 建立新节点连接newnode->_prev = prev; // ← 先指向原始前驱newnode->_next = cur; // → 指向当前节点// 更新相邻节点prev->_next = newnode; // 原始前驱→新节点cur->_prev = newnode; // 当前节点←新节点++_size;return iterator(newnode);}iterator erase(iterator pos){Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;cur->_next = next;cur->_prev = prev;delete cur;--_size;return iterator(next);}
而其原理,我们之前在学习链表时已经详细讲述过了,不清楚的同学可以看看以下链接
链表的增删查改
5.容量操作
size_t size()const{//int n = 0;//for (auto e : *this)//{// ++n;//}//return n;return _size;}
可以看到我们可以使用遍历的方式来实现对链表个数的统计,但是我们也可以直接添加一个成员变量size,在insert后++,erase后--;
三.完整代码实现
头文件
#pragma once
#include<iostream>
#include<list>
using namespace std;
namespace yiming
{template<class T>struct list_node//当不考虑用访问限定符做限制,所有成员公有的情况下,可以使用struct{T _data;list_node<T>* _next;list_node<T>* _prev;list_node(const T& x = T()):_data(x), _next(nullptr), _prev(nullptr){}};template<class T,class Ref,class Ptr>struct list_iterator{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;Node* _node;list_iterator(Node* node):_node(node){}~list_iterator()//迭代器不需要析构,由链表负责{}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}bool operator==(const Self& it)const{return _node == it._node;}bool operator!=(const Self& it)const{return _node != it._node;}};//template<class T>//struct list_const_iterator//{// typedef list_node<T> Node;// Node* _node;// list_const_iterator(Node* node)// :_node(node)// {// }// ~list_const_iterator()//迭代器不需要析构,由链表负责// {// }// const T& operator*()// {// return _node->_data;// }// list_const_iterator<T>& operator++()// {// _node = _node->_next;// return *this;// }// list_const_iterator<T>& operator++(int)// {// list_const_iterator<T> tmp(*this);// _node = _node->_next;// return tmp;// }// list_const_iterator<T>& operator--()// {// _node = _node->_prev;// return *this;// }// list_const_iterator<T>& operator--(int)// {// list_const_iterator<T> tmp(*this);// _node = _node->_prev;// return tmp;// }// bool operator==(const list_const_iterator<T>& it)const// {// return _node == it._node;// }// bool operator!=(const list_const_iterator<T>& it)const// {// return _node != it._node;// }//};//template<class T>class list{typedef list_node<T> Node;public: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);}void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;}list(){empty_init();}list(const list<T>& lt){//先让新创建的对象拥有自己的头尾节点empty_init();//遍历将后续节点接入for (const auto& e : lt){push_back(e);}}list(initializer_list<T> il){empty_init();for (const auto& e : il){push_back(e);}}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=( list<T> lt)//不使用引用,会调用拷贝构造,不用担心右侧值被修改{swap(lt);return *this;}//list<T>& operator=(const list<T>& lt)//{// if (this != <)//不是同一个节点时// {// clear();// for (const auto& e : lt)// {// push_back(e);// }// }// return *this;//}~list(){clear();delete _head;_head = nullptr;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}void push_back(const T& x){//Node* newnode = new Node(x);//Node* tail = _head->_prev;//tail->_next = newnode;//newnode->_prev = tail;//newnode->_next = _head;//_head->_prev = newnode;insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}iterator insert(iterator pos, const T& val){Node* cur = pos._node;Node* prev = cur->_prev; // 保存原始前驱节点Node* newnode = new Node(val);// 建立新节点连接newnode->_prev = prev; // ← 先指向原始前驱newnode->_next = cur; // → 指向当前节点// 更新相邻节点prev->_next = newnode; // 原始前驱→新节点cur->_prev = newnode; // 当前节点←新节点++_size;return iterator(newnode);}iterator erase(iterator pos){Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;cur->_next = next;cur->_prev = prev;delete cur;--_size;return iterator(next);}size_t size()const{//int n = 0;//for (auto e : *this)//{// ++n;//}//return n;return _size;}private:Node* _head;size_t _size=0;};
}
测试文件
#define _CRT_SECURE_NO_WARNINGS
#include"List.h"
namespace yiming
{template<class T>void print(const list<T><)//如何实现指向的内容不能修该但是能对it进行操作?{typename list<T>::const_iterator it = lt.begin();//类模版未实例化,不能去类模版中找后面的东西,typename告诉编译器是类型而非静态成员变量while (it != lt.end()){cout << *it << " ";++it;}cout << endl;}void test01(){yiming::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();lt.insert(it, 10);lt.push_back(6);lt.push_front(3);print(lt);yiming::list<int> lt2(lt);print(lt2);yiming::list<int> lt3={ 1,2,3,4,5 };print(lt3);}
}
int main()
{yiming::test01();return 0;
}
测试结果
本次分享就到这里结束了,后续会继续更新,感谢阅读!