STL库 —— list 的编写

目录

一、成员变量

​编辑

二、push_back 函数

三、迭代器 iterator

3.1 iterator 结构体

3.2 begin() 与 end() 函数

3.3 iterator 运算符重载

3.4 -> 的重载

3.5 const_iterator

四、测试代码

五、修饰符成员

5.1 insert 函数

5.2 erase 函数

5.3 push 函数

5.4 pop 函数

六、容量成员

6.1 size 函数

6.2 empty 函数

七、模板优化iterator


一、成员变量

	template<class T>struct ListNode{T _data;ListNode<T>* _prev;ListNode<T>* _next;ListNode(const T& x = T()):_data(x),_prev(nullptr),_next(nullptr){}};template<class T>class my_list{typedef ListNode<T> Node;private:Node* _head;//哨兵位头结点};

		my_list(){_head = new Node;_head->_next = _head;_head->_prev = _head;}

二、push_back 函数

为了测试方便,我们在这里先把 push_back 写出来。

		void push_back(T data){Node* NewNode = new Node(data);NewNode->_next = _head;NewNode->_prev = _head->_prev;_head->_prev->_next = NewNode;_head->_prev = NewNode;}

三、迭代器 iterator

3.1 iterator 结构体

这里的迭代器和之前的迭代器有所不同,诸如 string 、 vector ,他们的内存空间是连续的,迭代器的底层都是基于指针的,而 list 中,由于存储单元的随机性,迭代器不可以再是单纯的指针,而是像上面那样的结构体对象:

    template<class T>struct ListIterator{typedef ListNode<T> Node;typedef ListIterator<T> iterator;Node* _node;ListIterator(Node* node)//构造函数{_node = node;}};

3.2 begin() 与 end() 函数

然后,需要在 my_list 类中新添 begin 和 end 函数,因为他们都是服务于迭代器,所以他们的返回值类型应该是迭代器,还要注意,根据 STL 通用设计模式, end() 指向的位置不用于存放有效数据元素,而是作为遍历结束的标记。所以 end() 函数可以直接返回通过 _head 构造的迭代器,以此表示链表的末尾。

    template<class T>class my_list{typedef ListNode<T> Node;public:typedef ListIterator<T> iterator;iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}private:Node* _head;};

3.3 iterator 运算符重载

随后,为了使迭代器可以跑起来,需要再对 ++ 、 -- 、 != 等运算符进行重载,
其中,为了保持与内建类型行为一致,内建类型(如intfloat等)的前置自增和自减操作返回的是引用。自定义类型(如迭代器)模拟内建类型的行为,包括操作符的使用,有助于保持语言的一致性和直观性。
后置自增和自减不返回其引用的原因与前置自增自减的设计意图及使用方式有关。后置自增操作符的主要特点是先返回对象当前的值(在自增之前的状态),然后再对对象进行自增操作。这一行为决定了其返回类型应该是对象的值而不是引用
顺便,我们可以把 == 和 * 运算符都重载:

		typedef ListIterator<T> iterator;iterator& operator++()//前置++{_node = _node->_next;return *this;}iterator operator++(int)//后置++{iterator tmp(*this);_node = _node->_next;return tmp;}iterator& operator--()//前置--{_node = _node->_prev;return *this;}iterator operator--(int)//后置--{iterator tmp(*this);_node = _node->_prev;return tmp;}T& operator*(){return _node->_data;}bool operator!=(const iterator& it){return  _node != it._node;}bool operator==(const iterator& it){return _node == it._node;}

3.4 -> 的重载

当我们创建一个自定义类型,如 class A(此时在Flash命名空间内):

	class A{public:int a = 0;int b = 0;};

去测试 A 类型的 list 时,虽然 push_back 可以用,但是我们想遍历链表时,迭代器却不能用了:

	void test(){my_list<A> la;la.push_back({ 6, 12 });la.push_back({ 7, 18 });la.push_back({ 3, 22 });my_list<A>::iterator it = la.begin();while (it != la.end()){cout << *it << " ";it++;}cout << endl;}


错误的原因在于,系统不支持自定义类型的流插入,这里有两种解放方案:
1.重新重载一个 << 运算符
2.改变输出的格式 cout << (*it).a << (*it).b << " ";

		while (it != la.end()){cout << (*it).a << (*it).b << " ";it++;}

 但是究其本源,迭代器模拟的还是指针,如果是指针的话,就可以通过 -> 读取指针指向空间的数据,所以这里可以重载 -> 来达到模拟指针目的。

		T* operator->(){return &_node->_data;//->的优先级更高}

3.5 const_iterator

为了保证迭代器的内容不被修改,我们还要为迭代器添加 const ,但是根据所学知识,仅在函数返回值前加 const 是不能满足需求的,这只能保证迭代器本身不能被修改,此时就不可能自增和自减了,所以要新建一个 const_iterator 类,来模拟实现 const T* 指针的行为。

其中只有 operator* 与 operator-> 返回值不同。

template<class T>struct ListConstIterator{typedef ListNode<T> Node;typedef ListConstIterator<T> const_iterator;Node* _node;ListConstIterator(Node* node):_node(node){}// *itconst T& operator*(){return _node->_data;}// it->const T* operator->(){return &_node->_data;}// ++itconst_iterator& operator++(){_node = _node->_next;return *this;}const_iterator operator++(int){const_iterator tmp(*this);_node = _node->_next;return tmp;}const_iterator& operator--(){_node = _node->_prev;return *this;}const_iterator operator--(int){const_iterator tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};

此时在 my_list 类中在添加 cbegin 和 cend 函数:

        iterator begin() const{return iterator(_head->_next);}iterator end() const{return iterator(_head);}

四、测试代码

有了之前的代码,我们就可以进行统一的测试:

    void test_push_back(){my_list<int> l1;l1.push_back(1);l1.push_back(2);l1.push_back(3);l1.push_back(4);l1.push_back(5);my_list<int>::iterator it = l1.begin();while (it != l1.end()){cout << *it << endl;it++;}}

五、修饰符成员

上面的代码就可以让我们基本理清 list 的行为,有助于写下面的函数。

5.1 insert 函数

库中的 insert 支持的是迭代器访问,此时 pos 是上面的迭代器结构体, pos._node 才是此时要访问的节点。pos 位置前的节点的下一个节点是新插入的节点,pos位置是新插入节点后面的节点。

		void insert(iterator pos, T data){Node* NewNode = new Node(data);Node* prev = pos._node->_prev;Node* cur = pos._node;//节点位置: prev Newnode curprev->_next = NewNode;NewNode->_prev = prev;NewNode->_next = cur;cur->_prev = NewNode;}

5.2 erase 函数

erase 函数更加简单,其不需要创建新节点,仅需控制指针的指向即可。

		void erase(iterator pos){Node* prev = pos._node->_prev;Node* next = pos._node->_next;//prev pos nextprev->_next = next;next->_prev = prev;delete pos._node;}

5.3 push 函数

有了 insert 函数, push_back 函数就可以直接复用,因为是尾插,且传入的 pos 位置是插入位置的下一个节点, end 函数指向的是哨兵位节点,所以 pos 直接取到 end 即可。

		void push_back2(T data){insert(end(), data);}

同 push_back 使用 end 作为 pos 一样, begin 返回的是哨兵位的下一个节点,所以传入位置直接取到 begin 即可。

		void push_front(T data){insert(begin(), data);}

5.4 pop 函数

因为没有重载运算符 - ,所以 pop_back 传值时只能使用自减操作, end 指向的是哨兵位,所以需要使用 end 前面的位置,标准库中并没有实现减操作的重载,且效率很低,所以没有必要手搓。

		void pop_back(){erase(--end());}void pop_front(){erase(begin());}

六、容量成员

6.1 size 函数

`std::list`的`size()`成员函数返回容器中元素的数目。在C++11及之后的标准中,`std::list::size()`的时间复杂度为常数时间O(1)。这是通过内部维护一个表示元素数量的计数器来实现的。
每当添加或删除元素时,这个计数器就会相应地更新。
因此,`size()`函数的实现通常看起来像这样:

        size_t size() const {return _size;  // _size是一个成员变量,用来追踪当前链表中元素的数量}

这里的`_size`是容器的一个私有成员变量,其在构造函数中初始化,在向容器添加元素或从容器中移除元素时被更新。

但是要注意,在使用复用函数时,如上的 "push_front" "pop_back" 时, _size 不需要进行自增或自减操作,这样可能会自增自减两次,导致错误。

6.2 empty 函数

		bool empty(){return _size == 0;}

七、模板优化iterator

上面的 iterator 与 const_iterator 重复度太高,此时,就可以使用模板进行优化,让编译器根据传入的参数进行实例化:

template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> iterator;Node* _node;};

 与之前学的模板不同的是,传入的参数增加了,传入的参数变成了引用和指针:

    template<class T>class my_list{typedef ListNode<T> Node;public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/615974.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PE文件的分析和构造超详细过程

本文详细讲述如何从0构造一个PE文件&#xff0c;运行该文件会弹出一个HelloPE的窗口 目录 预备知识 1. 构造DOS头IMAGE_DOS_HEADER 1.1 构造DOS_MZ头 1.2 构造DOS_STUB 2、构造PE头IMAGE_NT_HEADERS 248字节 2.1 signature 2.2 IMAGE_FILE_HEADER 2.3 IMAGE_OPTI…

iOS开发之为什么需要引用计数

iOS开发之为什么需要引用计数 在iOS开发中&#xff0c;Objective-C与Swift语言都是通过引用计数进行内存管理&#xff0c;实际上Python、Ruby、C等语言也提供了基于引用计数的内存管理方式&#xff0c;它们有一个共同点&#xff0c;那就是都是面向对象的编程语言。 引用计数可…

LeetCode-72. 编辑距离【字符串 动态规划】

LeetCode-72. 编辑距离【字符串 动态规划】 题目描述&#xff1a;解题思路一&#xff1a;动规五部曲解题思路二&#xff1a;动态规划【版本二】解题思路三&#xff1a;0 题目描述&#xff1a; 给你两个单词 word1 和 word2&#xff0c; 请返回将 word1 转换成 word2 所使用的最…

java项目之校园兼职系统(ssm框架+mysql数据库+文档)

项目简介 校园兼职系统的主要使用者分为&#xff1a;管理员&#xff1a;首页、个人中心、专业管理、商家管理、热门兼职管理、学生管理、兼职接单管理、学生咨询管理、兼职任务管理、完成评价管理、管理员管理、系统管理等模块信息的查看及相应操作&#xff1b;学生&#xff1…

·13·1dawwd

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

使用 Python 标记具有相同名称的条目

如果大家想在 Python 中标记具有相同名称的条目&#xff0c;可以使用字典&#xff08;Dictionary&#xff09;或集合&#xff08;Set&#xff09;来实现。这取决于你们希望如何存储和使用这些条目。下面我将提供两种常见的方法来实现这个目标。 1、问题背景 在处理数据时&…

Unity让地图素材遮挡人物

点击编辑/项目设置/图形&#xff0c;透明度排序模式设置自定义轴&#xff0c;透明度排序轴Y设置为1其他为0。 此时人物和地图素材的图层排序相等&#xff0c;当人物的高度大于地图素材时&#xff0c;人物则被遮挡。

【软考中级】软件设计师考点分布

文章目录 软考官网资格设置软考报考流程 【软件设计师】考点分布选择题考点分布案例题考点分布 软考官网 中国计算机技术职业资格网&#xff1a;https://www.ruankao.org.cn/ 官网报名平台&#xff1a;https://bm.ruankao.org.cn/sign/welcome 资格设置 计算机软件计算机网…

AI预测体彩排3第2弹【2024年4月13日预测--第1套算法开始计算第2次测试】

各位小伙伴&#xff0c;今天实在抱歉&#xff0c;周末回了趟老家&#xff0c;回来比较晚了&#xff0c;数据今天上午跑完后就回老家了&#xff0c;晚上8点多才回来&#xff0c;赶紧把预测结果发出来吧&#xff0c;虽然有点晚了&#xff0c;但是咱们前面说过了&#xff0c;目前的…

C语言-----结构体详解

前面已经向大家介绍过一点结构体的知识了&#xff0c;这次我们再来深度了解一下结构体。结构体是能够方便表示一个物体具有多种属性的一种结构。物体的属性可以转换为结构体中的变量。 1.结构体类型的声明 1.1 结构体的声明 struct tag {member-list;//结构体成员变量 }vari…

ssm047网上服装销售系统+jsp

网上服装销售系统系统 摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于网上服装销售系统系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了网上服装销售系统…

架构设计参考项目系列主题:新零售SaaS架构:客户管理系统架构设计

什么是客户管理系统? 客户管理系统,也称为CRM(Customer Relationship Management),主要目标是建立、发展和维护好客户关系。 CRM系统围绕客户全生命周期的管理,吸引和留存客户,实现缩短销售周期、降低销售成本、增加销售收入的目的,从而提高企业的盈利能力和竞争力。 …