C++ : STL容器之list剖析

在这里插入图片描述

STL容器之list剖析

  • 一、几个重要接口
    • (一)push_back 与 emplace_back
    • (二)sort
      • 1、系统中的sort
      • 2、list中的sort
    • (三)splice
    • (四)unique 和 merge
      • 1、unique
      • 2、merge
  • 二、list的模拟实现
    • (一)结点类的实现
    • (二)迭代器的实现
      • 1、->运算符重载
    • (三)list实现
  • 三、结束语

一、几个重要接口

(一)push_back 与 emplace_back

push_back emplace_back的用法大致是一致的,只是二者在一种特定情况下 emplace_back 相比于 push_back 更优一些。

class Pos
{
public:int _row;int _col;Pos(int row = 0, int col = 0):_row(row), _col(col){cout << "Pos(int row, int col)" << endl;}Pos(const Pos& p):_row(p._row), _col(p._col){cout << "Pos(const Pos& p)" << endl;}
};void test03() {list<Pos> lt;// 构造+拷贝构造Pos p1(1, 1);lt.push_back(p1);//lt.push_back((2, 2));    //这种写法不对,//隐式类型转换针对的是构造函数,这里是括号运算符lt.push_back(Pos(2, 2));lt.push_back({3,3});lt.emplace_back(p1);lt.emplace_back(Pos(2, 2));//lt.emplace_back({ 3,3 });// 直接构造lt.emplace_back(3, 3);
}

在这里插入图片描述

我们采用push_back往链表尾部插入元素,可以分为三种情况:

1、先构造一个Pos类型,然后调用拷贝构造
2、使用匿名对象构造,在调用拷贝构造。
3、使用initializer_list初始化,在拷贝构造。

如果我们将其换成emplace_back,那么前面两种情况和push_back相同,而不再支持initializer_list,直接调用构造即可,因此这种情况更优。

(二)sort

list中实现了一个排序sort,我们知道在标准库里面也有一个全局的sort,为什么还要单独实现呢?其实list容器无法使用这个sort,这里我们需要引入迭代器的分类。

在这里插入图片描述

前面的迭代器我们曾经分为iterator,const_iterator,reverse_iterator, const_reverse_iterator这几种,下面我们按照新的方式来分类迭代器。

1、单项迭代器:只能正向访问,支持++
2、双向迭代器:可以双向访问支持++, –
3、随机迭代器:支持++, --, +, -

系统中sort采取的是双向迭代器,故而不能使用。因此单独实现了这个sort函数。

在这里插入图片描述

1、系统中的sort

我们只需要给出随机迭代器的首尾迭代器,就可以对容器进行排序,默认情况下给出的是按照升序排列,如果想降序,可以使用greater类对象。
在这里插入图片描述

void test04() {vector<int> v1 = { 1,20,3,-4,5 };for (auto e : v1){cout << e << " ";}cout << endl;//sort(v1.begin(), v1.end());sort(v1.begin(), v1.end(), greater<int>());for (auto e : v1){cout << e << " ";}cout << endl;
}

在这里插入图片描述

2、list中的sort

在这里插入图片描述
在这里插入图片描述

void test05() {/*greater<int> gt;lt1.sort(gt);*/list<int> lt1 = { 1,54,23,5,53,34 };lt1.sort(greater<int>());for (auto e : lt1){cout << e << " ";}
}

在这里插入图片描述

(三)splice

将一个链表中的一个/多个结点转移到另外一个链表的position 位置处·。
在这里插入图片描述
在这里,我们还可以使用splice函数实现选择当前的一个结点,将它放在当前链表的链表头位置。

void test06()
{list<int> lt1 = { 1,2,3,4,5 };// LRUint x;while (cin >> x){auto pos = find(lt1.begin(), lt1.end(), x);if (pos != lt1.end()){lt1.splice(lt1.begin(), lt1, pos);}for (auto e : lt1){cout << e << " ";}cout << endl;}
}

在这里插入图片描述

(四)unique 和 merge

把这两个函数放在一起是因为在使用这两个函数之前都需要对list进行排序。

1、unique

将链表进行去重操作。

2、merge

将两个有序链表进行归并操作。

void test07() {list<int> l1({ 2,4,2,5,7,8,6,3,3 });list<int> l2({ 21,42,42,5,57,86,68,32,3 });l1.sort();l2.sort();l1.merge(l2);for (auto& x : l1) {cout << x << " ";}cout << endl;l1.unique();for (auto& x : l1) {cout << x << " ";}
}

在这里插入图片描述

二、list的模拟实现

(一)结点类的实现

考虑到泛型编程的特点,我们在创建默认构造的时候,使用临时对象充当缺省参数。

template<class T>
struct list_node {T _data;list_node* _prev;list_node* _next;list_node(const T& data = T()):_data(data), _prev(nullptr), _next(nullptr){};
};

(二)迭代器的实现

我们知道,在之前的stringvector中,我们使用了原生指针做迭代器,是因为它具有连续的物理空间,而链表的实现则需要使用到类。

在实现const_iterator的时候,不可以简单的用const list_iterator来定义,此时类里面的指针也不能够被改变。

我们知道,const迭代器希望访问的元素不能被修改,因此我们只需要保证&以及->得到的值不会改变即可。
在这里,std库里面有一个十分巧妙的方法,添加了两个模板参数,实现了const_iteratoriterator两个类的融合。

1、->运算符重载

->运算符返回的是链表结点类型元素的数值信息的地址

Ptr* operator->(){return &_node->_data;}

需要注意的是,我们在取相应位置的元素时,只需要写一个->即可拿到相应的数据。

void test03() {list<Pos> lt;// 构造+拷贝构造Pos p1(1, 1);lt.push_back(p1);// 为了可读性,特殊处理,省略了一个->auto it2 = lt.begin();cout << it2->_row << ":" << it2->_col << endl;cout << it2.operator->()->_row << ":" << it2.operator->()->_col << endl;
}

在这里插入图片描述

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){}Ref& operator* (){return _node->_data;}Ptr* operator->(){return &_node->_data;}Self& operator++(){_node = _node->_next;return *this;}Self& operator--(){_node = _node->_prev;return *this;}Self operator++(int){Self tmp(_node);_node = _node->_next;return tmp;}Self operator--(int){Self tmp(_node);_node = _node->_prev;return tmp;}bool operator!=(const Self& s){return s._node != _node;}
};

(三)list实现

list实现的是一个有头双向循环链表,维护两个值,_head_size

实现要点:
1、typedef 在这里相当于实现了一个内部类
2、迭代器的返回值一定要有构造函数,不可以直接返回结点,小编当时走火入魔以为可以隐式类型转换,发生错误。
3、建议多使用复用,在这里我们只用实现inserterase操作,即可完成其他操作的复写。同时,在维护_size时,只需要在这两个函数里面维护即可。

	template<class T>class list{typedef list_node<T> Node;public:/*typedef list_iterator<T> iterator;typedef list_const_iterator<T> const_iterator;*/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;_size = 0;}list(){empty_init();}// lt2(lt1)list(const list<T>& lt){empty_init();for (auto& e : lt){push_back(e);}}// lt2 = lt3//list& operator=(list lt)list<T>& operator=(list<T> lt){swap(lt);return *this;}~list(){clear();delete _head;_head = nullptr;}void swap(list<T>& tmp){std::swap(_head, tmp._head);std::swap(_size, tmp._size);}void clear(){auto it = begin();while (it != end()){it = erase(it);}}list(size_t n, const T& val = T()){empty_init();for (size_t i = 0; i < n; i++){push_back(val);}}void push_back(const T& x){/*Node* new_node = new Node(x);Node* tail = _head->_prev;tail->_next = new_node;new_node->_prev = tail;new_node->_next = _head;_head->_prev = new_node;*/insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_front(){erase(begin());}void pop_back(){erase(--end());}iterator insert(iterator pos, const T& val){Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;// prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;++_size;return iterator(newnode);}iterator erase(iterator pos){assert(pos != end());Node* del = pos._node;Node* prev = del->_prev;Node* next = del->_next;prev->_next = next;next->_prev = prev;delete del;--_size;return iterator(next);}private:Node* _head;size_t _size;};

三、结束语

介绍完vectorsting之后,防止过于繁琐,后面的文章都是重点讲解一些特殊成员对象。感谢小伙伴一如既往的支持!

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

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

相关文章

新手小白,如何研究货币相关性

研究货币对之间的相关性可以帮助交易者理解市场动态&#xff0c;从而优化交易策略。以下是一个详细的研究方向&#xff0c;包括每个步骤的代码&#xff0c;以及一些深入探索的建议。 研究方向 选择货币对&#xff1a;确定需要研究的两个货币对。 数据收集&#xff1a;获取选…

如何保证Redis和数据库的数据一致性

文章目录 0. 前言1. 补充知识&#xff1a;CP和AP2. 什么情况下会出现Redis与数据库数据不一致3. 更新缓存还是删除缓存4. 先操作缓存还是先操作数据库4.1 先操作缓存4.1.1 数据不一致的问题是如何产生的4.1.2 解决方法&#xff08;延迟双删&#xff09;4.1.3 最终一致性和强一致…

【Postman】如何导出导入数据文件?Postman链接分享?

方式一&#xff1a;postman分享链接 1.1 导出 1.2 导入 1.3 导入完成后删除分享的链接 方式二&#xff1a;postman导出导入json 2.1 导出 2.2 post导入json数据

基于asp.NET的图书借阅系统

文章目录 前言项目介绍技术介绍功能介绍核心代码数据库参考 系统效果图 前言 文章底部名片&#xff0c;获取项目的完整演示视频&#xff0c;免费解答技术疑问 项目介绍 随着科学技术水平的逐年发展&#xff0c;构建一个高效、便捷的图书借阅系统。解决传统图书馆借阅过程中存…

全面了解CAN总线协议

提及总线&#xff0c;总是让人联想到那些交错在一起的计算机电线。那么这些电线如何发挥功效呢&#xff1f;这还得配合总线协议的管理来使用。那么今天我们介绍的就是CAN总线协议。看看这个协议的含义和应用吧。 CAN总线协议基本概念 1. 报文 总线上的信息以不同格式的报文发…

工业以太网之战:EtherCAT是如何杀出重围的?

前言 EtherCAT 是一种开放的实时工业以太网协议&#xff0c;由德国倍福公司开发并在 2003 年 4 月的汉诺威工业博览会上首次亮相&#xff0c;目前由 EtherCAT 技术协会&#xff08;ETG&#xff09;进行维护和推广。经过 21 年的不断发展&#xff0c;EtherCAT 显示出极强的生命…

移动 Web核心笔记(二)

空间转换 空间&#xff1a;是从坐标轴角度定义的 X 、Y 和 Z 三条坐标轴构成了一个立体空间&#xff0c;Z 轴位置与视线方向相同。 空间转换也叫 3D转换 属性&#xff1a;transform 平移 /*单独设置 z轴效果不明显*/ transform: translate3d(x, y, z); transform: translateX(…

PostgreSQL学习笔记:PostgreSQL vs MySQL

PostgreSQL 和 MySQL 都是广泛使用的关系型数据库管理系统&#xff0c;它们有以下一些对比&#xff1a; 一、功能特性 1. 数据类型支持 PostgreSQL&#xff1a;支持丰富的数据类型&#xff0c;包括数组、JSON、JSONB、范围类型、几何类型等。对于复杂数据结构的存储和处理非…

多线程——单例模式

目录 前言 一、设计模式 二、饿汉模式 三、懒汉模式 1.单线程版 2.多线程版 结尾 前言 前面的几篇文章中介绍了多线程编程的基础知识&#xff0c;在本篇文章开始&#xff0c;就会利用前面的多线程编程知识来编写一些代码案例&#xff0c;从而使大家可以更好的理解运用多…

Cypress安装用命令安装

安装node 试一下&#xff0c;安装yarn 用命令安装Cypress 下面找个截图说&#xff1a;会给用给几个用例引导你怎么写测试脚本

阿里云 EMR Serverless Spark 版正式开启商业化

阿里云 EMR Serverless Spark 版已于2024年9月14日正式商业化售卖&#xff0c;本文将简要介绍 EMR Serverless Spark 的产品优势、应用场景、支持地域&#xff0c;及计费模式等。 EMR Serverless Spark 是一款云原生&#xff0c;专为大规模数据处理和分析而设计的全托管 Server…

基于JSP实习管理系统【附源码】

基于SSM的学生管理系统&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 系统概述 4.2系统功能结构设计 4.3数据库设计 4.3.1数据库E-R图设计 4.3.2 数据库表结构设计 5 系统实现 5.1管理员功能介绍 5.1.1管理员登录 5.1.2…

数字身份管理建设是传统社会向数字社会演进的核心关键

当前&#xff0c;新一轮科技革命和产业变革突飞猛进。科学技术尤其是以互联网、大数据、云计算、人工智能和区块链等为代表的数字技术正与社会交往、社会服务、社区建设、社会治理等领域不断渗透融合&#xff0c;社会正在由人与环境构成的物理关系总和向“万物数字化”和万物互…

重磅!望繁信科技与德勤中国签署战略合作协议

2022年&#xff0c;望繁信科技与德勤中国签署流程挖掘战略合作协议&#xff01;双方强强联合&#xff0c;在拓展流程优化市场、推动企业数智融合等领域展开深度合作&#xff0c;持续共建具有全球影响力的流程挖掘新生态。 根据协议内容&#xff0c;双方计划在未来三年内&#x…

软考攻略/超详细/系统集成项目管理工程师/基础知识分享18

6.5数据分析及应用 6.5.1 数据集成&#xff08;掌握&#xff09; 数据集成就是将驻留在不同数据源中的数据进行整合&#xff0c;向用户提供统一的数据视图&#xff0c;使得用户能以透明的方式访问数据。 WebServices技术是一个面向访问的分布式计算模型&#xff0c;它的本质是…

RabbitMQ 入门(六)SpringAMQP五种消息类型(Direct Exchange)

一、发布订阅-DirectExchange&#xff08;路由模式&#xff09; 在Fanout模式中&#xff0c;一条消息&#xff0c;会被所有订阅的队列都消费。但是&#xff0c;在某些场景下&#xff0c;我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。 Direct Exchan…

关键链项目管理是什么?它如何优化传统项目管理?

在项目管理的世界里&#xff0c;方法论千千万万&#xff0c;但真正能够提升项目效率和成功率的却并不多见。关键链项目管理&#xff08;Critical Chain Project Management, CCPM&#xff09;作为一种独特且高效的管理方式&#xff0c;正在被越来越多的企业所采用。相较于传统的…

NAND 数据恢复:使用 VNR 闪存数据恢复软件提取闪存转储中的块

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据恢复、数据备份解决方案与服务&#xff0c;并针对企业面临的数据安全风险&#xff0c;提供专业的相关数据安全培训。 天津鸿萌科贸发展有限公司是专业 NAND 闪存数据恢复工具 VN…

linux下离线安装jq工具

故障现象&#xff1a; 当前使用的是CentOS7&#xff0c; 使用sudo yum install jq这个命令后&#xff0c;总是报错 Loaded plugins: fastestmirror, langpacks Determining fastest mirrors ... Cannot find a valid baseurl for repo: extras/7/x86_64 使用uname -a查看我当…

Yolov10训练的餐盘菜品目标检测软件(包含源码及数据集)

本文摘要 摘要&#xff1a;本文主要使用YOLOV10深度学习框架自训练了一个“餐盘菜品目标检测模型”&#xff0c;基于此模型使用PYQT5实现了一款界面软件用于功能演示。让您可以更好的了解和学习&#xff0c;该软件支持图片、视频以及摄像头进行目标检测&#xff0c;本系统所涉…