• 实现list的简单模型


    list 是一个带头的双向循环链表

    结点构造

    参数用匿名对象,方便直接构造结点。

    1. template<class T>
    2. struct list_node
    3. {
    4. list_node* _next;
    5. list_node* _prev;
    6. T _data;
    7. list_node(const T& val = T())
    8. :_next(nullptr)
    9. ,_prev(nullptr)
    10. ,_data(val)
    11. {}
    12. };

    迭代器的构造

    迭代器本质上是结点的指针,但是 list 结点不是连续的,因此原生指针的特性必须由我们自己实现

    迭代器是指针,结点不属于自己,不能释放结点,因此没有析构函数 

    参照源码逻辑:这里是写一个迭代器的模板,下面传参时可以根据类型的不同,来确定是实例化普通的迭代器还是 const 类型的迭代器。

    用 self 代替模板,方便模板参数类型的替换

    Ref 和 Ptr

    注意 * 和 -> 这两个符号的重载:it 是一个 int* 

    *it = 10;

    * 是对指针解引用,直接可以改变数据,因此返回的是数据的引用;

    it->data;

    -> 直接对指针指向的内容进行访问,返回的应该是存储数据的地址

    而引用 Ref 和 Ptr 的目的就是为了区分迭代器类型

    1. template<class T, class Ref, class Ptr>
    2. struct _list_iterator
    3. {
    4. typedef list_node Node;
    5. typedef _list_iterator self;
    6. Node* _node;
    7. //没有析构函数--结点不属于迭代器,不能被迭代器释放
    8. //拷贝构造和赋值重载--默认生成的浅拷贝就可以
    9. _list_iterator(Node* node)
    10. :_node(node)
    11. {}
    12. //T& 或者 const T&
    13. Ref operator*()
    14. {
    15. return _node->_data;
    16. }
    17. //T* 或者 const T*
    18. Ptr operator->()
    19. {
    20. return &(operator*());
    21. }
    22. self& operator++() //前置++
    23. {
    24. _node = _node->_next;
    25. return *this;
    26. }
    27. self& operator++(int) //后置++
    28. {
    29. self tmp(*this);
    30. _node = _node->_next;
    31. return tmp;
    32. }
    33. self& operator--() //前置--
    34. {
    35. _node = _node->_prev;
    36. return *this;
    37. }
    38. self& operator--(int) //后置--
    39. {
    40. self tmp(*this);
    41. _node = _node->_prevt;
    42. return tmp;
    43. }
    44. bool operator!=(const self& it)
    45. {
    46. return _node != it._node;
    47. }
    48. bool operator==(const self& it)
    49. {
    50. return _node == it._node;
    51. }
    52. };

    list 的对迭代器的实例化

    在 list 中,通过控制模板参数的不同,来完成传参时,对不同迭代器的实例化。

    1. template<class T>
    2. class list
    3. {
    4. typedef list_node Node;
    5. public:
    6. typedef _list_iterator iterator;
    7. typedef _list_iteratorconst T&,const T*> const_iterator;
    8. const_iterator begin() const
    9. {
    10. return const_iterator(_head->_next);
    11. }
    12. const_iterator end() const
    13. {
    14. return const_iterator(_head);
    15. }
    16. iterator begin()
    17. {
    18. return iterator(_head->_next);
    19. }
    20. iterator end()
    21. {
    22. return iterator(_head);
    23. }
    24. list()
    25. {
    26. _head = new Node();
    27. _head->_next = _head;
    28. _head->_prev = _head;
    29. }
    30. private:
    31. Node* _head;
    32. };

    打印函数传参

    1. void print_list(const list<int>& lt)
    2. {
    3. list<int>::const_iterator it = lt.begin();
    4. while(it != lt.end())
    5. {
    6. cout << *it << " ";
    7. ++it;
    8. }
    9. cout << endl;
    10. }

    在构造 list 时,指定迭代器的三个模板参数类型,将 const 和普通迭代器区分开来;

    例如当我们想打印出一个 list 中的数据时,给打印函数传过去的是 const list& lt

    那么此时用到 const_iterator 匹配到的就是如下模板:

    typedef  _list_iteratorconst T&,const T*> const_iterator;

    这样构建出的迭代器就不能修改数据了。 

    list 的构造函数

    list 带头双向循环链表,我们在构造时主要是开辟头结点,令前后指针指向自身

    empty_init 是初始化一个 list 的头结点

    支持迭代器区间构造

    现代写法完成拷贝构造

    1. list()
    2. {
    3. _head = new Node();
    4. _head->_next = _head;
    5. _head->_prev = _head;
    6. }
    7. //拷贝构造传统写法
    8. //list(const list& lt)
    9. //{
    10. // _head = new Node();
    11. // _head->_next = _head;
    12. // _head->_prev = _head;
    13. // for(auto e: lt)
    14. // {
    15. // push_back(e);
    16. // }
    17. //}
    18. //
    19. void empty_init()
    20. {
    21. _head = new Node();
    22. _head->_next = _head;
    23. _head->_prev = _head;
    24. }
    25. template<class InputIterator>
    26. list(InputIterator first,InputIterator last)
    27. {
    28. empty_init();
    29. while(first != last)
    30. {
    31. push_back(*first);
    32. ++first;
    33. }
    34. }
    35. void swap(list& lt)
    36. {
    37. std::swap(_head,lt._head);
    38. }
    39. //现代写法
    40. list(const list& lt)
    41. {
    42. empty_init();
    43. list tmp(lt.begin(),lt.end());
    44. swap(tmp);
    45. }
    46. list& operator=(list lt)
    47. {
    48. swap(lt);
    49. return *this;
    50. }

    插入删除

    尾插尾删和头插头删可以复用插入和删除

    插入时总是插入在指定位置的前面,不会造成迭代器失效的问题

    删除会造成迭代器失效,野指针问题,返回的是删除位置的下一个位置

    1. void push_back(const T& x)
    2. {
    3. insert(end(),x);
    4. }
    5. void push_front(const T& x)
    6. {
    7. insert(begin(),x);
    8. }
    9. void pop_back()
    10. {
    11. erase(--end());
    12. }
    13. void pop_front()
    14. {
    15. erase(begin());
    16. }
    17. //插入在pos位置之前
    18. void insert(iterator pos,const T& x)
    19. {
    20. Node* newnode = new Node(x);
    21. Node* cur = pos._node;
    22. Node* prev = cur->_prev;
    23. newnode->_next = cur;
    24. cur->_prev = newnode;
    25. prev->_next = newnode;
    26. newnode->_prev = prev;
    27. }
    28. iterator erase(iterator pos)
    29. {
    30. assert(pos != end());
    31. Node* cur = pos._node;
    32. Node* prev = cur->_prev;
    33. Node* next = cur->_next;
    34. prev->_next = next;
    35. next->_prev = prev;
    36. delete cur;
    37. return next;
    38. }

    析构函数

    调用析构函数前,先要将数据删除,再释放头结点

    clear 可以删除全部结点,刚好保留头结点

    1. ~list()
    2. {
    3. clear();
    4. delete _head;
    5. _head = nullptr;
    6. }
    7. void clear()
    8. {
    9. iterator it = begin();
    10. while(it != end())
    11. {
    12. it = erase(it);
    13. }
    14. }

     

  • 相关阅读:
    【Python】这篇文章能让你明白经验模态分解(EMD)——EMD在python中的实现方法
    【解惑】时间规划,Linq的Aggregate函数在计算会议重叠时间中的应用
    云音箱服务器对接
    当社恐成为技术面试官
    Python程序流程控制结构
    【27】c++设计模式——>迭代器模式(1)
    ElasticSearch学习笔记:内容有点多,但很实用(包含安装、使用、安全、部署)
    Matlab神经网络工具箱——一个例子搞定神经网络算法
    【mybatis注解开发+二级缓存】
    动态规划-爬楼梯
  • 原文地址:https://blog.csdn.net/weixin_53316121/article/details/126531448