当前位置: 首页 > article >正文

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的迭代器

函数名功能介绍
beginendbegin:首元素的位置;end:下一个元素的位置
rbeginrendc指const,cbegin和cend指向的内容不能修改
cbegincend反向迭代器,rbegin从end开始,rend从begin开始,其++和–的方向相反
crbegincrend与前一个功能相同,但指向的内容不能修改

3.list的容量与元素访问

函数名函数声明功能介绍
sizesize_type size() const返回list中有效元素个数
frontreference front()
const_reference front() const
返回list第一个元素的引用。
backreference back()
const_reference back() const
返回list最后一个元素的引用。

4.list容器的修改

函数名函数声明功能介绍
sizesize_type size() const返回list中有效元素个数
frontreference front()
const_reference front() const
返回list第一个元素的引用。
backreference 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,本质相当于写了一个类模板,编译器生成了两个类。

List1

list迭代器的比较:

注意 operator==operator!= 不是用 list 的值进行比较的,因为 list 中可以有多个相同的值,在迭代器遍历时会出现问题。实际它们的比较是通过地址来比较的,这样能确保在遍历的时候不会出问题。

4.2 begin和end

begin和end的const问题:

begin()end() 使用在 const 对象上时,不能直接对其进行 const 修饰。原因是对迭代器加 const 修饰,不能修改的是迭代器,但我们的目的是让迭代器指向的内容不能被修改,所以迭代器有 iteratorconst_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容器&#xff0c;其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身&#xff0c;这样的容器被统称为序列式容器。 2. list容器 list 是用双向带哨兵位头节点的循环链表实现的。list 通过类模板…...

二、OpenSM排障----实战生产

目录 一、确认 OpenSM 服务端故障的步骤 1. 检查客户端与服务器的连通性 2. 检查客户端 InfiniBand 接口状态 3. 检查子网管理器状态 4. 检查拓扑信息 5. 检查路由表 二、客户端日志位置及查看方法 1. 系统日志 2. OpenSM 客户端日志 3. 内核日志 4. 性能计数器日志…...

Windows 找不到文件gpedit.msc,没有组策略编辑器,解决办法附上

windows10和11都通用。是不是有人告诉你家庭版本没有gpedit.msc&#xff0c;没有组策略编辑器&#xff1f;这压根就是某软玩的小把戏。Win10/11家庭版可通过修改文件后缀新建bat脚本&#xff0c;添加组策略包&#xff0c;以管理员身份运行后&#xff0c;输入gpedit.msc即可打开…...

基于Docker-compose的禅道部署实践:自建MySQL与Redis集成及故障排查指南

基于Docker-compose的禅道部署实践&#xff1a;自建MySQL与Redis集成及故障排查指南 禅道镜像版本&#xff1a;easysoft/zentao:21.4 Redis版本&#xff1a;redis:6.2.0 Mysql版本&#xff1a;mysql:8.0.35 文章目录 **基于Docker-compose的禅道部署实践&#xff1a;自建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&#xff08;人工智能生成内容&#xff09; &#xff08;一&#xff09;定义与内涵 &#xff08;二&#xff09;核心技术与应用场景 &#xff08;三&#xff09;优势与挑战 二、AICG&#xff08;计算机图形学中的人工智能&#xff09; &#xff08;一&#x…...

基于 openEuler 构建 LVS-DR 群集

一、 对比 LVS 负载均衡群集的 NAT 模式和 DR 模式&#xff0c;比较其各自的优势 。 二、 基于 openEuler 构建 LVS-DR 群集。 一 NAT 模式 部署简单&#xff1a;NAT 模式下&#xff0c;所有的服务器节点只需要连接到同一个局域网内&#xff0c;通过负载均衡器进行网络地址转…...

Python练习11-20

题目&#xff1a;古典问题&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子总数为多少&#xff1f; 题目&#xff1a;判断101-200之间有多少…...

Java 实战:在图片指定位置贴二维码或条形码生成海报

在很多业务场景中&#xff0c;我们需要将生成的二维码或条形码贴到一张已有的图片上&#xff0c;从而生成一张完整的海报。下面就教你如何使用 Java 实现这个功能&#xff0c;我们将借助 ZXing 库生成二维码或条形码&#xff0c;使用 Java 的 java.awt 和 javax.imageio 包来处…...

深入指南:在IDEA中启用和使用DeepSeek

引言 2025年的春节可以说是人工智能在中国史上飘红的一段历史时刻&#xff0c;年后上班的第一天&#xff0c;便马不停蹄的尝试新技能。今天的科技在飞速发展&#xff0c;编程领域的人工智能工具犹如雨后春笋般涌现。其中&#xff0c;DeepSeek 则以其卓越的性能和智能化的功能&a…...

SPSS—回归分析

一、如何选择 回归方法的选择是根据因变量的类型进行选择&#xff0c;无论自变量是哪种类型。 如果因变量&#xff0c;也就是目标变量是连续的数值型变量&#xff0c;当自变量也是连续数值型&#xff0c;研究自变量是否对因变量有影响。选择普通的线性回归即可&#xff0c;根…...

深入剖析 Burp Suite:Web 应用安全测试利器

目录 前言 一、Burp Suite 简介 二、功能组件详解 三、使用场景 四、安装与使用步骤 安装步骤 使用步骤 五、总结 前言 在网络安全的复杂版图中&#xff0c;Burp Suite 宛如一颗璀璨的明珠&#xff0c;以其强大的功能和广泛的适用性&#xff0c;成为众多安全从业者不可…...

unity学习37:新版的动画器:动画状态机 Animator

目录 1 给游戏物体添加&#xff0c;新版的动画器 Animator 2 关于 Animator 3 创建 动画器的控制器 Animator Controller 4 打开动画编辑器 Animator 5 动画编辑器 还是Animation 5.1 创建新的动画 5.2 创建第2个动画 5.3 测试2个动画均可用 6 再次打开动画编辑器 A…...

LC-搜索二维矩阵II、相交链表、反转链表、回文链表、环形链表、环形链表ll

搜索二维矩阵II 方法&#xff1a;从右上角开始搜索 我们可以从矩阵的右上角开始进行搜索。如果当前元素 matrix[i][j] 等于 target&#xff0c;我们直接返回 true。如果 matrix[i][j] 大于 target&#xff0c;说明 target 只能出现在左边的列&#xff0c;所以我们将列指针向左…...

【MySQL在Centos 7环境安装】

文章目录 一. 卸载不必要的环境二. 检查系统安装包三. 卸载这些默认安装包四. 获取mysql官⽅yum源五. 安装mysql yum 源&#xff0c;对⽐前后yum源六. 看看能不能正常⼯作七. 安装mysql服务八. .查看配置⽂件和数据存储位置九. 启动服务并查看服务是否存在十. 登陆⽅法十一. 设…...

计算机网络-MPLS基础概念

早期传统IP报文依赖路由器查询路由表转发&#xff0c;但由于硬件技术存在限制导致转发性能低&#xff0c;路由器的查表转发成为了网络数据转发的瓶颈。因此旨在提高路由器转发速度的MPLS&#xff08;Multi-Protocol Label Switching&#xff0c;多协议标签交换&#xff09; 被提…...

南京某企业面试题整理

[1]. 消息队列主要是传递什么消息的&#xff1f; 消息队列主要用于在不同的应用程序或服务之间传递异步消息。这些消息通常包含需要处理的数据或事件通知&#xff0c;使得系统能够解耦、提高并发性和可伸缩性。 消息队列中传递的常见消息类型包括&#xff1a; 事件通知&#…...

NO.18十六届蓝桥杯备战|循环嵌套|乘法表|斐波那契|质数|水仙花数|(C++)

循环嵌套 循环嵌套的使⽤ while &#xff0c; do while &#xff0c; for &#xff0c;这三种循环往往会嵌套在⼀起才能更好的解决问题&#xff0c;就是我们所说的&#xff1a;循环嵌套。这三种循环都可以任意嵌套使⽤ ⽐如&#xff1a; 写⼀个代码&#xff0c;打印⼀个乘法⼝…...

数字化转型的深度思考与最佳实践

引言&#xff1a;数字化转型的时代背景 在数字经济迅猛发展的今天&#xff0c;数字化转型已成为企业生存和发展的必由之路。根据IDC的报告&#xff0c;到2025年&#xff0c;全球数字经济规模将超过23万亿美元&#xff0c;占GDP的比重将超过50%。然而&#xff0c;数字化转型并非…...

支持向量机原理

支持向量机&#xff08;简称SVM&#xff09;虽然诞生只有短短的二十多年&#xff0c;但是自一诞生便由于它良好的分类性能席卷了机器学习领域。如果不考虑集成学习的算法&#xff0c;不考虑特定的训练数据集&#xff0c;尤其在分类任务中表现突出。在分类算法中的表现SVM说是排…...

LLM - 理解 DeepSeek 的 GPRO (分组相对策略优化) 公式与源码 教程(2)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/145640762 GPRO&#xff0c;即 Group Relative Policy Optimization&#xff0c;分组相对的策略优化&#xff0c;是 PPO(Proximal Policy Optimiz…...

通过用户名和密码登录服务器有哪些方法

通过用户名和密码登录到服务器的方式取决于你使用的工具和协议。以下是几种常见的方法&#xff1a; 1. 使用 SSH 登录到 Linux 服务器 你可以通过 SSH&#xff08;Secure Shell&#xff09;使用用户名和密码连接到远程服务器。通常&#xff0c;你会使用 ssh 命令来进行连接。…...

基于springboot 以及vue前后端分离架构的求职招聘系统设计与实现

基于springboot 以及vue前后端分离架构的求职招聘系统设计与实现 随着互联网技术的飞速发展&#xff0c;求职招聘行业也在不断发生变革。传统的求职招聘方式往往存在着信息不对称、效率低下、交易成本高等问题&#xff0c;导致企业的招聘成本增加&#xff0c;求职者的体验下降…...

Spring Boot整合协同过滤算法,实现个性化推荐

1. 引言 在这篇文章中&#xff0c;我们将展示如何使用 Spring Boot 框架与 协同过滤算法 相结合来构建一个简单的推荐系统。推荐系统广泛应用于电商、电影推荐、社交平台等领域。协同过滤算法通过分析用户行为&#xff0c;找出相似的用户或者物品&#xff0c;从而实现个性化推荐…...

自己部署 DeepSeek 助力 Vue 开发:打造丝滑的时间线(Timeline )

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 自己…...

光谱相机在天文学领域的应用

天体成分分析 恒星成分研究&#xff1a;恒星的光谱包含了其大气中各种元素的吸收和发射线特征。通过光谱相机精确测量这些谱线&#xff0c;天文学家能确定恒星大气中氢、氦、碳、氮、氧等元素的含量。如对太阳的光谱分析发现&#xff0c;太阳大气中氢元素占比约 71%&#xff0…...

深度卷积神经网络实战海洋动物图像识别

本文采用深度卷积神经网络作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv11以其高效的特征提取能力&#xff0c;在多个图像分类任务中展现出卓越性能。本研究针对5种海洋动物数据集进行训练和优化&#xff0c;该数据集包含丰富的海…...

MySQL-mysql zip安装包配置教程

网上的教程有很多&#xff0c;基本上大同小异。但是安装软件有时就可能因为一个细节安装失败。我也是综合了很多个教程才安装好的&#xff0c;所以本教程可能也不是普遍适合的。 安装环境&#xff1a;win11 1、下载zip安装包&#xff1a; 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底层数据结构——整数集合

文章目录 定义内部实现升级升级的好处提升灵活性节约内存 降级总结 定义 整数集合&#xff08;intset&#xff09;是集合键的底层实现之一&#xff0c;当一个集合只包含整数值元素&#xff0c;并且这个集合的元素数量不多时&#xff0c;Redis就会使用整数集合作为集合键的底层…...