C++ STL容器之list的使用及复现
list
1. 序列式容器
vector、list、deque、forward_list(C++11 )等STL容器,其底层为线性序列的数据结构,里面存储的是元素本身,这样的容器被统称为序列式容器。
2. list容器
list 是用双向带哨兵位头节点的循环链表实现的。list 通过类模板来满足不同类型的数据,在使用时必须显示实例化调用。
3. list的成员函数
3.1 list的成员函数的介绍
| 函数声明 | 功能介绍 |
|---|---|
| list< T > l() | 构造一个空的list |
| list< T > l(n,val) | 构造一个内容为n个val的list |
| list< T > l (l1.iterator1(),l1.iterator2()) | 构造一个从l1的iterator1到iterator2的list |
| list< T >l(l1) | 拷贝构造一个l1的list |
2. list的迭代器
| 函数名 | 功能介绍 |
|---|---|
| begin和end | begin:首元素的位置;end:下一个元素的位置 |
| rbegin和rend | c指const,cbegin和cend指向的内容不能修改 |
| cbegin和cend | 反向迭代器,rbegin从end开始,rend从begin开始,其++和–的方向相反 |
| crbegin和crend | 与前一个功能相同,但指向的内容不能修改 |
3.list的容量与元素访问
| 函数名 | 函数声明 | 功能介绍 |
|---|---|---|
| size | size_type size() const | 返回list中有效元素个数 |
| front | reference front() const_reference front() const | 返回list第一个元素的引用。 |
| back | reference back() const_reference back() const | 返回list最后一个元素的引用。 |
4.list容器的修改
| 函数名 | 函数声明 | 功能介绍 |
|---|---|---|
| size | size_type size() const | 返回list中有效元素个数 |
| front | reference front() const_reference front() const | 返回list第一个元素的引用。 |
| back | reference back() const_reference back() const | 返回list最后一个元素的引用。 |
4. list容器的重构
4.1 list迭代器的问题
迭代器的构造函数问题:
迭代器需要一个构造函数来初始化迭代器的状态,但迭代器不需要写拷贝构造,因为迭代器指向的节点不属于迭代器的类。
list迭代器的多参数特点:
在重载 list 的迭代器时,需要考虑普通迭代器的重载和 const 迭代器的重载。但如果只使用一个模板参数,就需要把普通迭代器的整段代码都拷贝下来单独做一份 const 迭代器的类,造成代码的冗余。(因为函数重载必须返回值相同,cons t修饰不算是重载,所以只能多做一个类)
stl中的list容器使用了多个模板参数来规避这个问题:
template<class T, class Ref, class Ptr>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node;ListIterator(Node* node):_node(node){}// *it//T& operator*()Ref operator*(){return _node->_data;}// it->//T* operator->()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){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}
};
迭代器使用多个模板解决 const,本质相当于写了一个类模板,编译器生成了两个类。

list迭代器的比较:
注意 operator== 和 operator!= 不是用 list 的值进行比较的,因为 list 中可以有多个相同的值,在迭代器遍历时会出现问题。实际它们的比较是通过地址来比较的,这样能确保在遍历的时候不会出问题。
4.2 begin和end
begin和end的const问题:
begin() 和 end() 使用在 const 对象上时,不能直接对其进行 const 修饰。原因是对迭代器加 const 修饰,不能修改的是迭代器,但我们的目的是让迭代器指向的内容不能被修改,所以迭代器有 iterator 和 const_iterator 两个版本。
template<class T>
class list
{typedef ListNode<T> Node;
public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;iterator begin(){return _head->_next;}iterator end(){return _head;}const_iterator begin() const{return _head->_next;}const_iterator end() const{return _head;}
}
begin和end的匿名对象问题:
在重构 list 的迭代器时候,begin() 和 end() 的实现原理是匿名对象,而匿名对象会调用对象的拷贝构造:
template<class T>
class list
{typedef ListNode<T> Node;
public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;iterator begin(){return iterator(_head->_next);//也可以返回有名对象//iterator it(_head->next);//return it;}iterator end(){return iterator(_head);}}
甚至,可以把 iterator() 也省略掉,原理是单参数的构造函数支持隐式类型转换:
template<class T>
class list
{typedef ListNode<T> Node;
public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;iterator begin(){return _head->_next;}iterator end(){return _head;}}
4.3 operator->的重载
list容器对 operator-> 进行了重载,它的重构代码是:
Ptr operator->()
{return &_node->_data;
}
在实际使用时,编译器为提高代码的可读性,还对其进行了优化:
list<A>::iterator it = lt.begin();
while (it != lt.end())
{//*it += 10;//cout << (*it)._a1 << ":" << (*it)._a2 << endl;cout << it->_a1 << ":" << it->_a2 << endl;cout << it.operator->()->_a1 << ":" << it.operator->()->_a2 << endl;++it;
}
在
it->_a1这段代码中,它的底层是it.operator->()->_a1,按理它的调用应该是it->->_a1,但编译器将第二个箭头拿掉,使得代码整体更好读。
4.4 完整代码
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;namespace mylist
{// List的节点类template<class T>struct ListNode{ListNode(const T& val = T()):_pPre(nullptr),_pNext(nullptr),_val(val){}ListNode<T>* _pPre;ListNode<T>* _pNext;T _val;};//List的迭代器类template<class T, class Ref, class Ptr>class ListIterator{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;public:ListIterator(PNode pNode = nullptr):_pNode(pNode){}//ListIterator(const Self& l)//{};Ref& operator*(){return _pNode->_val;}Ptr operator->(){return &_pNode->_val;}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self temp(*this);_pNode = _pNode->_pNext;return temp;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self& operator--(int){Self temp(*this);_pNode = _pNode->_pPre;return temp;}bool operator!=(const Self& l){return _pNode != l._pNode;}bool operator==(const Self& l){return _pNode == l._pNode;}PNode _pNode;};//list类template<class T>class list{typedef ListNode<T> Node;typedef Node* PNode;public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T&> const_iterator;public:///// List的构造list(){CreateHead();}list(int n, const T& value = T()){CreateHead();for (int i =0 ; i < n; i++){push_back(value);}}template <class Iterator>list(Iterator first, Iterator last){CreateHead();Iterator it = first;while (it != last){push_back(*it);++it;}}list(const list<T>& l){CreateHead();const_iterator it = l.begin();while (it != l.end()){push_back(*it);++it;}}list<T>& operator=(list<T> l){swap(l);return *this;}~list(){clear();delete _pHead;_size = 0;}///// List Iteratoriterator begin(){return _pHead->_pNext;}iterator end(){return _pHead;}const_iterator begin() const{return _pHead->_pNext;}const_iterator end() const{return _pHead;}///// List Capacitysize_t size()const{return _size;}bool empty()const{return _size == 0;}// List AccessT& front(){return *(_pHead->_pNext);}const T& front()const{return *(_pHead->_pNext);}T& back(){return *(_pHead->_pPre);}const T& back()const{return *(_pHead->_pPre);}// List Modifyvoid push_back(const T& val) { insert(end(), val); }void pop_back() { erase(--end()); }void push_front(const T& val) { insert(begin(), val); }void pop_front() { erase(begin()); }// 在pos位置前插入值为val的节点iterator insert(iterator pos, const T& val){Node* cur = pos._pNode;Node* newnode = new Node(val);newnode->_pNext = cur;newnode->_pPre = cur->_pPre;cur->_pPre->_pNext = newnode;cur->_pPre = newnode;_size++;return iterator(newnode);}// 删除pos位置的节点,返回该节点的下一个位置iterator erase(iterator pos){Node* cur = pos._pNode;Node* prev = cur->_pPre;Node* next = cur->_pNext;prev->_pNext = next;next->_pPre = prev;delete cur;_size--;return iterator(next);}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}void swap(list<T>& l){std::swap(_pHead, l._pHead);std::swap(_size, l._size);}private:void CreateHead(){_pHead = new Node;_pHead->_val = 0;_pHead->_pNext = _pHead;_pHead->_pPre = _pHead;}PNode _pHead;size_t _size=0;};
};
相关文章:
C++ STL容器之list的使用及复现
list 1. 序列式容器 vector、list、deque、forward_list(C11 )等STL容器,其底层为线性序列的数据结构,里面存储的是元素本身,这样的容器被统称为序列式容器。 2. list容器 list 是用双向带哨兵位头节点的循环链表实现的。list 通过类模板…...
二、OpenSM排障----实战生产
目录 一、确认 OpenSM 服务端故障的步骤 1. 检查客户端与服务器的连通性 2. 检查客户端 InfiniBand 接口状态 3. 检查子网管理器状态 4. 检查拓扑信息 5. 检查路由表 二、客户端日志位置及查看方法 1. 系统日志 2. OpenSM 客户端日志 3. 内核日志 4. 性能计数器日志…...
Windows 找不到文件gpedit.msc,没有组策略编辑器,解决办法附上
windows10和11都通用。是不是有人告诉你家庭版本没有gpedit.msc,没有组策略编辑器?这压根就是某软玩的小把戏。Win10/11家庭版可通过修改文件后缀新建bat脚本,添加组策略包,以管理员身份运行后,输入gpedit.msc即可打开…...
基于Docker-compose的禅道部署实践:自建MySQL与Redis集成及故障排查指南
基于Docker-compose的禅道部署实践:自建MySQL与Redis集成及故障排查指南 禅道镜像版本:easysoft/zentao:21.4 Redis版本:redis:6.2.0 Mysql版本:mysql:8.0.35 文章目录 **基于Docker-compose的禅道部署实践:自建MySQL与…...
C++20 多线程机制
C++20 多线程机制 C++20 多线程机制说明总结C++20 多线程机制说明 C++20 引入了许多新的多线程特性,增强了标准库对并发编程的支持。以下是 C++20 中多线程编程的关键特性和用法:C++20 多线程核心特性 std::jthread:std::jthread 是 C++20 引入的新线程类,与 std::thread …...
AIGC与AICG的区别解析
目录 一、AIGC(人工智能生成内容) (一)定义与内涵 (二)核心技术与应用场景 (三)优势与挑战 二、AICG(计算机图形学中的人工智能) (一&#x…...
基于 openEuler 构建 LVS-DR 群集
一、 对比 LVS 负载均衡群集的 NAT 模式和 DR 模式,比较其各自的优势 。 二、 基于 openEuler 构建 LVS-DR 群集。 一 NAT 模式 部署简单:NAT 模式下,所有的服务器节点只需要连接到同一个局域网内,通过负载均衡器进行网络地址转…...
Python练习11-20
题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 题目:判断101-200之间有多少…...
Java 实战:在图片指定位置贴二维码或条形码生成海报
在很多业务场景中,我们需要将生成的二维码或条形码贴到一张已有的图片上,从而生成一张完整的海报。下面就教你如何使用 Java 实现这个功能,我们将借助 ZXing 库生成二维码或条形码,使用 Java 的 java.awt 和 javax.imageio 包来处…...
深入指南:在IDEA中启用和使用DeepSeek
引言 2025年的春节可以说是人工智能在中国史上飘红的一段历史时刻,年后上班的第一天,便马不停蹄的尝试新技能。今天的科技在飞速发展,编程领域的人工智能工具犹如雨后春笋般涌现。其中,DeepSeek 则以其卓越的性能和智能化的功能&a…...
SPSS—回归分析
一、如何选择 回归方法的选择是根据因变量的类型进行选择,无论自变量是哪种类型。 如果因变量,也就是目标变量是连续的数值型变量,当自变量也是连续数值型,研究自变量是否对因变量有影响。选择普通的线性回归即可,根…...
深入剖析 Burp Suite:Web 应用安全测试利器
目录 前言 一、Burp Suite 简介 二、功能组件详解 三、使用场景 四、安装与使用步骤 安装步骤 使用步骤 五、总结 前言 在网络安全的复杂版图中,Burp Suite 宛如一颗璀璨的明珠,以其强大的功能和广泛的适用性,成为众多安全从业者不可…...
unity学习37:新版的动画器:动画状态机 Animator
目录 1 给游戏物体添加,新版的动画器 Animator 2 关于 Animator 3 创建 动画器的控制器 Animator Controller 4 打开动画编辑器 Animator 5 动画编辑器 还是Animation 5.1 创建新的动画 5.2 创建第2个动画 5.3 测试2个动画均可用 6 再次打开动画编辑器 A…...
LC-搜索二维矩阵II、相交链表、反转链表、回文链表、环形链表、环形链表ll
搜索二维矩阵II 方法:从右上角开始搜索 我们可以从矩阵的右上角开始进行搜索。如果当前元素 matrix[i][j] 等于 target,我们直接返回 true。如果 matrix[i][j] 大于 target,说明 target 只能出现在左边的列,所以我们将列指针向左…...
【MySQL在Centos 7环境安装】
文章目录 一. 卸载不必要的环境二. 检查系统安装包三. 卸载这些默认安装包四. 获取mysql官⽅yum源五. 安装mysql yum 源,对⽐前后yum源六. 看看能不能正常⼯作七. 安装mysql服务八. .查看配置⽂件和数据存储位置九. 启动服务并查看服务是否存在十. 登陆⽅法十一. 设…...
计算机网络-MPLS基础概念
早期传统IP报文依赖路由器查询路由表转发,但由于硬件技术存在限制导致转发性能低,路由器的查表转发成为了网络数据转发的瓶颈。因此旨在提高路由器转发速度的MPLS(Multi-Protocol Label Switching,多协议标签交换) 被提…...
南京某企业面试题整理
[1]. 消息队列主要是传递什么消息的? 消息队列主要用于在不同的应用程序或服务之间传递异步消息。这些消息通常包含需要处理的数据或事件通知,使得系统能够解耦、提高并发性和可伸缩性。 消息队列中传递的常见消息类型包括: 事件通知&#…...
NO.18十六届蓝桥杯备战|循环嵌套|乘法表|斐波那契|质数|水仙花数|(C++)
循环嵌套 循环嵌套的使⽤ while , do while , for ,这三种循环往往会嵌套在⼀起才能更好的解决问题,就是我们所说的:循环嵌套。这三种循环都可以任意嵌套使⽤ ⽐如: 写⼀个代码,打印⼀个乘法⼝…...
数字化转型的深度思考与最佳实践
引言:数字化转型的时代背景 在数字经济迅猛发展的今天,数字化转型已成为企业生存和发展的必由之路。根据IDC的报告,到2025年,全球数字经济规模将超过23万亿美元,占GDP的比重将超过50%。然而,数字化转型并非…...
支持向量机原理
支持向量机(简称SVM)虽然诞生只有短短的二十多年,但是自一诞生便由于它良好的分类性能席卷了机器学习领域。如果不考虑集成学习的算法,不考虑特定的训练数据集,尤其在分类任务中表现突出。在分类算法中的表现SVM说是排…...
LLM - 理解 DeepSeek 的 GPRO (分组相对策略优化) 公式与源码 教程(2)
欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://spike.blog.csdn.net/article/details/145640762 GPRO,即 Group Relative Policy Optimization,分组相对的策略优化,是 PPO(Proximal Policy Optimiz…...
通过用户名和密码登录服务器有哪些方法
通过用户名和密码登录到服务器的方式取决于你使用的工具和协议。以下是几种常见的方法: 1. 使用 SSH 登录到 Linux 服务器 你可以通过 SSH(Secure Shell)使用用户名和密码连接到远程服务器。通常,你会使用 ssh 命令来进行连接。…...
基于springboot 以及vue前后端分离架构的求职招聘系统设计与实现
基于springboot 以及vue前后端分离架构的求职招聘系统设计与实现 随着互联网技术的飞速发展,求职招聘行业也在不断发生变革。传统的求职招聘方式往往存在着信息不对称、效率低下、交易成本高等问题,导致企业的招聘成本增加,求职者的体验下降…...
Spring Boot整合协同过滤算法,实现个性化推荐
1. 引言 在这篇文章中,我们将展示如何使用 Spring Boot 框架与 协同过滤算法 相结合来构建一个简单的推荐系统。推荐系统广泛应用于电商、电影推荐、社交平台等领域。协同过滤算法通过分析用户行为,找出相似的用户或者物品,从而实现个性化推荐…...
自己部署 DeepSeek 助力 Vue 开发:打造丝滑的时间线(Timeline )
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 自己…...
光谱相机在天文学领域的应用
天体成分分析 恒星成分研究:恒星的光谱包含了其大气中各种元素的吸收和发射线特征。通过光谱相机精确测量这些谱线,天文学家能确定恒星大气中氢、氦、碳、氮、氧等元素的含量。如对太阳的光谱分析发现,太阳大气中氢元素占比约 71%࿰…...
深度卷积神经网络实战海洋动物图像识别
本文采用深度卷积神经网络作为核心算法框架,结合PyQt5构建用户界面,使用Python3进行开发。YOLOv11以其高效的特征提取能力,在多个图像分类任务中展现出卓越性能。本研究针对5种海洋动物数据集进行训练和优化,该数据集包含丰富的海…...
MySQL-mysql zip安装包配置教程
网上的教程有很多,基本上大同小异。但是安装软件有时就可能因为一个细节安装失败。我也是综合了很多个教程才安装好的,所以本教程可能也不是普遍适合的。 安装环境:win11 1、下载zip安装包: MySQL8.0 For Windows zip包下载地址…...
Python爬虫实战:获取笔趣阁图书信息,并做数据分析
注意:以下内容仅供技术研究,请遵守目标网站的robots.txt规定,控制请求频率避免对目标服务器造成过大压力! 1. 环境准备与反爬策略 python import requests from bs4 import BeautifulSoup import pandas as pd import re import time import random from fake_useragent …...
redis底层数据结构——整数集合
文章目录 定义内部实现升级升级的好处提升灵活性节约内存 降级总结 定义 整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层…...
