【C++】-- STL之list模拟实现(二)

简介: 【C++】-- STL之list模拟实现

9.clear( )

只清除所有节点内容,不能删除头节点,如果删除头节点那么链表就不存在了,是链表的析构函数需要完成的操作

1.    void clear()
2.    {
3.      iterator it = begin();
4.      while (it != end())
5.      {
6.        erase(it++);//erase需要传参迭代器,即位置
7.      }
8.    }

10.push_front( )

头插

1. //头插   
2. void push_front(const T& x)
3.    {
4.      insert(begin(), x);
5.    }

11.push_back( )

尾插

1.    //尾插
2.    void push_back(const T& x)
3.    {
4.      insert(end()--, x); 
5.    }

12.pop_front( )

头删

1.    //头删
2.    void pop_front()
3.    {
4.      erase(begin());
5.    }

13.pop_back( )

尾删

1.    //尾删
2.    void pop_back()
3.    {
4.      erase(--end());
5.    }

14.empty( )

判空

1.    //判空
2. bool empty()
3.    {
4.      return end() == begin();
5.    }

15 size( )

求节点个数

1.    //求节点个数
2. size_t size()
3.    {
4.      iterator it = begin();
5.      size_t sz = 0;
6.      while (it != end())//时间复杂度O(N)
7.      {
8.        it++;
9.        sz++;
10.       }
11. 
12.       return sz;
13.     }

五、完整代码段

016-list.h

1. #pragma once
2. #include<iostream>
3. #include<assert.h>
4. using namespace std;
5. 
6. namespace delia
7. {
8.  template<class T>
9.  struct _list_node
10.   {
11.     T _val;
12.     _list_node<T>* _prev;
13.     _list_node<T>* _next;
14. 
15.     _list_node(const T& val = T())
16.       :_val(val) 
17.       , _prev(nullptr)
18.       , _next(nullptr)
19.     {}; 
20.   };
21. 
22. 
23.   //节点的指针原生行为不满足迭代器定义,在这里,迭代器通过类来封装节点的指针重载运算符来控制
24. 
25.   //对于T&,类模板实例化出两个类,一个是T&类,一个是const T&类,同理,T*也一样。
26.   //使用template<class T,class Ref,class Ptr>类模板就会实例化出来两个类,一个是T,T&, T*的,一个是T,const T&, const T*的   
27.   //template _list_iterator<T, T&, T*> _iterator;
28.   //template _list_iterator<T, const T&, const T*> const_iterator;
29. 
30.   //这样就不需要再去定义一个如下const的迭代器类:
31.   //template<class T>
32.   //struct _list_const_iterartor
33.   //{
34.   //  typedef _list_node<T> node;
35.   //  typedef _list_const_iterartor<T> self;
36. 
37.   //  node* _pnode;
38.   //}
39.   // 
40. 
41.   template<class T,class Ref,class Ptr>
42.   struct _list_iterator//使用_list_iterator类来封装node*
43.   {
44.     typedef _list_node<T> node;
45.     typedef _list_iterator<T,Ref,Ptr> self;
46. 
47.     node* _pnode;
48. 
49.     //构造函数
50.     _list_iterator(node* pnode)
51.       :_pnode(pnode)
52.     {}
53. 
54.     //拷贝构造、赋值运算符重载、析构函数,编译器默认生成即可
55. 
56.     //解引用,返回左值,是拷贝,因此要用引用返回
57.     Ref operator*()
58.     {
59.       return _pnode->_val;
60.     }
61. 
62.     //结构体指针解引用,返回节点值的引用
63.     Ptr operator->()
64.     {
65.       return &_pnode->_val;
66.     }
67. 
68.     //!=重载
69.     bool operator!=(const self& s) const
70.     {
71.       return _pnode != s._pnode;
72.     }
73. 
74.     //==重载
75.     bool operator==(const self& s) const
76.     {
77.       return _pnode == s._pnode;
78.     }
79. 
80.     //前置++  it.operator(&it)
81.     self& operator++()
82.     {
83.       _pnode = _pnode->_next;
84.       return *this;
85.     }
86. 
87.     //后置++ 返回++之前的值  it.operator(&it,0)
88.     self operator++(int)
89.     {
90.       self tmp(*this);
91.       _pnode = _pnode->_next;
92.       return tmp;
93.     }
94. 
95.     //前置--  it.operator(&it)
96.     self& operator--()
97.     {
98.       _pnode = _pnode->prev;
99.       return *this;
100.    }
101. 
102.    //后置++ 返回++之前的值  it.operator(&it,0)
103.    self operator--(int)//临时对象不能用引用返回,所以self没有加&
104.    {
105.      self tmp(*this);
106.      _pnode = _pnode->_prev;
107.      return tmp;
108.    }
109.  };
110. 
111.  template<class T>
112.  class list
113.  {
114.    typedef _list_node<T> node;
115.  public:
116.    typedef _list_iterator<T,T&,T*> iterator;//重命名迭代器
117.    typedef _list_iterator<T, const T&, const T*> const_iterator;//重命名const迭代器
118. 
119.    //构造函数
120.    list()
121.    {
122.      _head = new node;//会调_list_node的构造函数
123.      _head->_next = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
124.      _head->_prev = _head;//整个链表只有头节点,先构造一个没有实际节点的链表
125.    }
126. 
127.    //拷贝构造 lt1(lt)
128.    list(const list<T>& lt)
129.    {
130.      //1.先构造一个只有头节点的空list:创建头节点,头节点的前一个是自己,头节点的后一个是自己
131.      _head = new node;
132.      _head->_next = _head;
133.      _head->_prev = _head;
134. 
135.      //2.将lt的元素全部尾插到新链表
136.      for (const auto& e : lt)
137.      {
138.        push_back(e);
139.      }
140.    }
141. 
142.    赋值运算符重载  lt1 = lt  传统写法
143.    //list<T>& operator=(list<T>& lt)
144.    //{
145.    //  if(this != lt)
146.    //  {
147.    //    for (auto& e : lt)
148.    //    {
149.    //      push_back(e);
150.    //    }
151.    //  }
152.    //}
153. 
154.    //赋值运算符重载  lt1 = lt  现代写法
155.    list<T>& operator=(list<T>& lt)
156.    {
157.      swap(_head, lt._head);
158.      return *this;
159.    }
160. 
161.    //析构
162.    ~list()
163.    {
164.      clear();
165.      delete _head;
166.      _head = nullptr;
167.    }
168. 
169.    iterator begin()
170.    {
171.      return iterator(_head->_next);
172.    }
173. 
174.    iterator end()
175.    {
176.      return iterator(_head);//尾节点的下一个节点位置即头节点
177.    }
178. 
179.    const_iterator begin() const
180.    {
181.      return const_iterator(_head->_next);
182.    }
183. 
184.    const_iterator end() const
185.    {
186.      return const_iterator(_head);//尾节点的下一个节点位置即头节点
187.    }
188. 
189.    //插入节点
190.    void insert(iterator pos, const T& x)
191.    {
192.      assert(pos._pnode);
193.      node* newnode = new node(x);//构造节点
194. 
195.      node* prev = pos._pnode->_prev;//为方便后续操作,给插入位置的前一个节点起了个名字,pos是迭代器对象
196. 
197.      //插入节点
198.      newnode->_next = pos._pnode;
199.      pos._pnode->_prev = newnode;
200.      prev->_next = newnode;
201.      newnode->_prev = prev;
202. 
203.    }
204. 
205.    //删除节点
206.    iterator erase(iterator pos)
207.    {
208.      assert(pos._pnode);//判断该位置节点是否存在
209.      assert(pos != end());//end()是最后一个节点的下一个节点位置,也就是头节点,头节点不能删,需要断言
210. 
211.      node* prev = pos._pnode->_prev;//pos位置节点的前一个节点
212.      node* next = pos._pnode->_next;//pos位置节点的后一个节点
213. 
214.      //删除节点
215.      delete pos._pnode;
216.      prev->_next = next;
217.      next->_prev = prev;
218. 
219.      return iterator(next);//删除之后pos失效,把下一个位置的迭代器给它
220.    }
221. 
222.    void clear()
223.    {
224.      iterator it = begin();
225.      while (it != end())
226.      {
227.        erase(it++);
228.      }
229.    }
230. 
231.    //头插
232.    void push_front(const T& x)
233.    {
234.      insert(begin(), x);
235.    }
236. 
237.    //尾插
238.    void push_back(const T& x)
239.    {
240.      insert(end()--, x); 
241.    }
242. 
243.    //头删
244.    void pop_front()
245.    {
246.      erase(begin());
247.    }
248. 
249.    //尾删
250.    void pop_back()
251.    {
252.      erase(--end());
253.    }
254. 
255.    //判空
256.    bool empty()
257.    {
258.      return _head->_next == _head;
259.    }
260. 
261.    //求节点个数
262.    size_t size()
263.    {
264.      iterator it = begin();
265.      size_t sz = 0;
266.      while (it != end())//时间复杂度O(N)
267.      {
268.        it++;
269.        sz++;
270.      }
271. 
272.      return sz;
273.    }
274.  private:
275.    node* _head;
276.  };
277. 
278.  void PrintList(const list<int>& lt)
279.  {
280.    list<int>::const_iterator it = lt.begin();
281.    while (it != lt.end())
282.    {
283.      cout << it._pnode->_val << " ";
284.      it++;
285.    }
286.    cout << endl;
287.  }
288. 
289.  void test_list1()
290.  {
291.    list<int> l1;
292.    l1.push_back(5);
293.    l1.push_back(8);
294.    l1.push_back(20);
295.    l1.push_back(9);
296. 
297.    PrintList(l1);
298. 
299.    list<int> l2;
300.    l2 = l1;
301.    PrintList(l2);
302. 
303.    cout << endl;
304.  }
305. }

016-test.cpp

1. #include "016-list.h"
2. #include<list>
3. using namespace std;
4. 
5. int main()
6. {
7.  delia::test_list1();
8.  return 0;
9. }
相关文章
|
2天前
|
算法 C++ 容器
模拟实现c++中的list模版
模拟实现c++中的list模版
|
24天前
|
C++ 容器
【c++丨STL】stack和queue的使用及模拟实现
本文介绍了STL中的两个重要容器适配器:栈(stack)和队列(queue)。容器适配器是在已有容器基础上添加新特性或功能的结构,如栈基于顺序表或链表限制操作实现。文章详细讲解了stack和queue的主要成员函数(empty、size、top/front/back、push/pop、swap),并提供了使用示例和模拟实现代码。通过这些内容,读者可以更好地理解这两种数据结构的工作原理及其实现方法。最后,作者鼓励读者点赞支持。 总结:本文深入浅出地讲解了STL中stack和queue的使用方法及其模拟实现,帮助读者掌握这两种容器适配器的特性和应用场景。
55 21
|
2月前
|
编译器 C语言 C++
【c++丨STL】list模拟实现(附源码)
本文介绍了如何模拟实现C++中的`list`容器。`list`底层采用双向带头循环链表结构,相较于`vector`和`string`更为复杂。文章首先回顾了`list`的基本结构和常用接口,然后详细讲解了节点、迭代器及容器的实现过程。 最终,通过这些步骤,我们成功模拟实现了`list`容器的功能。文章最后提供了完整的代码实现,并简要总结了实现过程中的关键点。 如果你对双向链表或`list`的底层实现感兴趣,建议先掌握相关基础知识后再阅读本文,以便更好地理解内容。
42 1
|
2月前
|
算法 C语言 C++
【c++丨STL】list的使用
本文介绍了STL容器`list`的使用方法及其主要功能。`list`是一种双向链表结构,适用于频繁的插入和删除操作。文章详细讲解了`list`的构造函数、析构函数、赋值重载、迭代器、容量接口、元素访问接口、增删查改操作以及一些特有的操作接口如`splice`、`remove_if`、`unique`、`merge`、`sort`和`reverse`。通过示例代码,读者可以更好地理解如何使用这些接口。最后,作者总结了`list`的特点和适用场景,并预告了后续关于`list`模拟实现的文章。
69 7
|
2月前
|
存储 编译器 C++
C++ initializer_list&&类型推导
在 C++ 中,`initializer_list` 提供了一种方便的方式来初始化容器和传递参数,而右值引用则是实现高效资源管理和移动语义的关键特性。尽管在实际应用中 `initializer_list&&` 并不常见,但理解其类型推导和使用方式有助于深入掌握现代 C++ 的高级特性。
28 4
|
3月前
|
存储 编译器 C语言
【c++丨STL】vector的使用
本文介绍了C++ STL中的`vector`容器,包括其基本概念、主要接口及其使用方法。`vector`是一种动态数组,能够根据需要自动调整大小,提供了丰富的操作接口,如增删查改等。文章详细解释了`vector`的构造函数、赋值运算符、容量接口、迭代器接口、元素访问接口以及一些常用的增删操作函数。最后,还展示了如何使用`vector`创建字符串数组,体现了`vector`在实际编程中的灵活性和实用性。
135 4
|
3月前
|
C语言 C++ 容器
【c++丨STL】string模拟实现(附源码)
本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。
140 5
|
2月前
|
存储 编译器 C语言
【c++丨STL】vector模拟实现
本文深入探讨了 `vector` 的底层实现原理,并尝试模拟实现其结构及常用接口。首先介绍了 `vector` 的底层是动态顺序表,使用三个迭代器(指针)来维护数组,分别为 `start`、`finish` 和 `end_of_storage`。接着详细讲解了如何实现 `vector` 的各种构造函数、析构函数、容量接口、迭代器接口、插入和删除操作等。最后提供了完整的模拟实现代码,帮助读者更好地理解和掌握 `vector` 的实现细节。
67 0
|
2天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
1月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
68 19