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

【C++学习】list的使用及模拟实现

🐱作者:一只大喵咪1201
🐱专栏:《C++学习》
🔥格言:你只管努力,剩下的交给时间!
tu

list的使用及模拟实现

  • 😼构造函数
    • 🐵模拟实现
  • 😼迭代器
    • 🐵普通迭代器(iterator)模拟实现
    • 🐵const迭代器(const_iterator)模拟实现
    • 🐵箭头(->)运算符重载
  • 😼赋值运算符重载
    • 🐵模拟实现
  • 😼析构函数
    • 🐵析构函数存在的必要性
  • 😼容量和访问操作
    • 🐵容量操作
    • 🐵访问操作
  • 😼增删查改操作
  • 😼其他几个常用接口
  • 😼list和vector优劣比较
  • 😼总结

list和vector一样,也是一个非常常用的数据结构,它和vector就像左手和右手的关系,优略势互补,它的本质是一个带头双向循环列表,下面本喵来给大家详细介绍一下list。

😼构造函数

图
官方提供的构造函数有四个,其中包括拷贝构造函数,如上图所示。

void list_test1()
{list<int> lt1;list<int> lt2(5, 1);vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);list<int> lt3(v1.begin() + 1, v1.end() - 1);list<int> lt4(lt2);
}

图
通过调试的监视窗口可以看到,使用四种方法都能成功创建list对象。

为了更好的理解list,下面来模拟实现一下它。

🐵模拟实现

图
如上图,list是又一个又一个的节点构成,每个节点中,不仅存放数据,而且存放着前一个结点和后一个节点的指针。

所以我们需要创建一个类,这个类就是用来描述节点的。

namespace wxf
{template <class T>struct __list_node{__list_node* prev;//指向前一个节点的指针__list_node* next;//执行下一个节点的指针T data;//根据模板参数实例化,存放数据__list_node(const T& x):prev(nullptr),next(nullptr),data(x){}};
}

同样将我们自己模拟实现的部分放在命名空间wxf中。

  • 这里节点类创建时,使用的关键字是struct,而不是class。
  • 使用struct创建的类,默认所有成员都是公有的,如果使用class,默认所有成员是私有的,需要在所有成员变量之前加public。

每一个节点在创建时,需要进行初始化,所以在节点类中需要有一个构造函数,来初始化节点。

构造函数:

图
为了使用方便,将前面的节点类使用typedef重命名为node,在list类中,成员变量只有一个,就是节点的指针变量_head,也就是头节点的地址。

  • 类名<模板参数>等价于类型,所以在typedef的时候,只写类名__list_node是不行的。

每创建一个list对象的时候,首先要做的事情就是new一个头节点,由于此时list是空的,所以头节点的next和prev都是指向自己的,如上图绿色框中的代码所示。

push_back():

需要一个接口来向list中插入数据,这里先实现最常用的push_back。

图
每次尾插一个数据的时候,就需要动态开辟一个节点对象,然后再将_head,tail,newnode三个节点按照带头双向循环列表的规则进行链接。

图
如上图,在尾插一个新节点的时候,由原本绿色的链接关系变成了蓝色的链接关系。

图

成功的使用我们自己实现的构造函数创建了一个list对象,并且尾插了五个数字,如上图所示。

继续实现构造函数:

图
使用多个相同的数据来构造list对象的的构造函数,本质就是进行多次尾插,如上图中的红色框。

图
通过监视窗口看到,符合我们对预期。

每次构造一个对象的时候,必须先初始化头节点,为了简化代码,将头节点初始化的代码放在一个函数中。

图
在每个构造函数中调用这个初始化函数就可以进行头节点的初始化。

😼迭代器

先看它的使用:

void list_test2()
{list<int> lt1;lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);lt1.push_back(5);list<int>::iterator it1 = lt1.begin();while (it1 != lt1.end()){cout << *it1 << " ";++it1;}cout << endl;
}

tu
使用迭代器成功打印出了list中的每一个数据。

无论是哪种数据结构,都有迭代器,在前面我们学习vector和string的时候就学习到了迭代器,我们知道它是一个行为上像指针一样的东西,由于vector本身的物理结构是连续的,所以它的迭代器本质上就是一个指针。

但是list的物理结构是不连续的,它的本质注定不能是一个指针,下面本喵来详细介绍。

🐵普通迭代器(iterator)模拟实现

  • 迭代器有++或者–等行为,由于list的物理空间不连续,所以此时的++和–需要重新定义,也就是需要进行运算符重载。

为了让它行为上像指针一样,只能通过一个类来描述它,所以说,list的迭代器是一个类对象。

	template <class T>struct __list_iterator{typedef __list_node<T> node;node* _pnode;__list_iterator(node* p):_pnode(p){}};

创建一个迭代器类,如上面的代码,类中只有一个成员变量,用了存放节点的地址。同样为了能够直接访问,使用struct创建类。

同样,必须有构造函数,编译器自动生成的默认构造函数不能将pnode初始化为我们想要的,所以需要自己实现一个。

要想让list的迭代器也能像指针一样进行++或者–,需要对运算符进行重载。
图
如上图中,分别是前置的++和–以及厚置++和–的运算符重载。

  • 前置++返回的是++后的结果,所以返回this指针的内容,并且使用的是引用返回,因为这里的迭代器对象不会销毁。
  • 后缀++返回的是++前的结果,所以需要一个临时对象,来存放++之前的结果,然后再进行++,但是,这里不能用引用返回,临时对象出了作用域以后就会销毁。

上面四个运算符重载函数中,无论是返回类型,还行创建的临时变量,都需要写很长一个串类型名(__list_iterator),为了简化,我们这里将其重命名成一个简单的类型名。(这一步操作后面有大用处)

typedef __list_iterator<T> Self;

并且将前面代码中的__list_iterator替换为Self。

既然像指针一样,那迭代器就必须能解引用,因为list的迭代器是一个类对象,所以解引用同样需要进行运算符重载。

图
由于解引用以后会对list中的数据进行访问,所以使用引用返回。

图

上图中是对==和!=进行的运算符重载,这俩个符合也是非常常用的。

图
将迭代器本身处理好以后,还需要让它在list类域中能够找到,如上图红色框中代码,在list类中,将迭代器重命名为我们熟悉的iterator。

  • 这里的重命名必须设置为公有,否则我们无法创建itertator迭代器,因为私有在类外是无法访问的。

图
在list类中还需要提供begin和end接口,如上图所示,这是在list类中实现的,而不是在__list_iterator中实现的。

图
此时,我们模拟实现的迭代器同样可以正常使用了。

🐵const迭代器(const_iterator)模拟实现

当一个对象是被const修饰的时候,普通迭代器就不再适用了,需要用到const迭代器。

图
此时我们创建的是一个const对象,但是发现通过它的迭代器,该对象不仅可以访问,而且还可以修改,这和const的性质相悖,const修饰的对象也失去了它应有的意义。

我们首先要做的事情就是不能让它修改。此时就有一种改法:

const list<int>::iterator it = lt1.begin();

用const修饰it,此时不就行了吗?

答案是不行的。此时运行会报错,大家可以自己尝试一下。

  • 此时const修饰的是it本身,会导致it不能被修改,也就是it不能进行++和减减等操作。
  • 并且此时*it还行可以进行++。

这和我们使用const的初衷不符合,我们是为了防止修改it指向的对象,而不是it本身。

既然这样,我们让*it返回的数据不能被修改。

图
如上图,在迭代器类中,对*的运算符重载,返回的数据类型是const T&,此时就无法修改了。

道理是这么个道理,但是此时就存在了两个operator*运算符重载函数:

图
在运行的时候,会报错。

  • 返回类型不同不能构成远算符重载。

为了解决这个问题,需要再创建一个类,创建一个const_iterator的专属类。

图

此时普通迭代器类和const迭代器两个类,除了类名不一样以外,只有解引用运算符重载(operator*())不一样,其他都一样。

图

并且在list类中,也将两种迭代器typedef为我们熟悉的样子。

图
此时it就不能被修改了,符合了const的性质。

虽然实现了,但是总感觉有不舒服,因为太冗余了,就是因为一个函数的返回类型而重新写了一个类,可以采用下面的办法来解决。

图
迭代器的类模板,使用两个模板参数,解引用运算符重载的返回类型使用第二个模板参数Ref,如上图红色框所示。

图
在list类中,给迭代器类不同类型的模板参数,并且使用typedef进行重命名,如上图红色框中所示。

  • 此时,使用哪种迭代器,就会使用哪种模板参数进行实例化,相当于将前面我们写的俩个迭代器类的工作交给了编译器来完成。

图

同样还需要写上图中的两个成员函数在list类中,此时const迭代器才能正常的使用begin和end。

🐵箭头(->)运算符重载

图
当放入list的数据时一个结构体类型的时候,如上图红色框所示,此时想要打印出具体数据是做不到的,因为流插入运算符(<<)不支持打印结构体类型的数据,如上图中绿色框中所示。

要想打印,此时该怎么办呢?

图
可以像上图中那样,先将it解引用拿到结构体数据,然后使用点操作符打印出行和列。

既然迭代器it的行为像指针,理论上我们也可以通过箭头(->)操作符来访问结构体类型数据。

图
将像这样,但是此时很显然,是无法实现的,需要我们进行箭头运算符的重载。

图
在迭代器类中进行运算符重载,箭头运算符重载返回的是数据的地址,如上图所示。

图
此时成功打印出了一组坐标。而且使用的是箭头操作符,行为完全符合指针。

但是此时又有一个疑问:

图
将该运算符重载函数现实调用以后,如上图中的红色框,得到的是数据所在的地址,再加一个箭头,如上图中绿色框,才能访问到结构体中具体成员。这样来看,一共加起来两个箭头,但是直接使用重载后的运算符,只需要it->一个箭头就可以。

  • 为了提高可读性,编译器进行了简化,将原本的两个箭头简化成了一个箭头。

在箭头运算符重载函数中,返回类型是T*,同样的,如果是一个const对象呢?继续使用T又不符合const的性质,const T 返回类型又构不成重载,为了解决这个问题,可以参考两种迭代器的解决办法。

将迭代器类模板的模板参数再增加一个:

图

将模板参数的第三个参数作为operator->()运算符重载函数的返回类型。

图
在list类中,普通迭代器第三个模板参数就传T*,const迭代器,第三个模板参数就传const T*,让编译器根据具体情况去实例化。

图
前面本喵就说过,这个地方会有大用,当模板参数有了变化以后,只需要在红色框中改变就行,在后面用到类型的时候,由于typedef的存在,直接使用Self即可。

到这里,我们就将迭代器部分模拟完了,反向迭代器本喵就不再模拟了,我们仅是为了更好的学习迭代器,并不是为了造一个更好的轮子。

使用迭代器区间的构造函数:

😼赋值运算符重载

图

赋值运算符重载函数只有这一个,没有重载。

图
上图中,创建了lt1和lt2两个list对象,其中lt2是空的,然后将lt1的值全部赋值给lt2,从运行结果中可以看到,此时两个对象的值是一样的。

🐵模拟实现

在模拟实现赋值运算符重载之前,需要做一些准备工作,同样这些接口也是我们常用的,在这里就一并模拟实现了。

erase():

图
eraes的本质就是将prev,next直接链接上,从而跳过pos,并且最后释放pos所在的节点。为了防止出现迭代器失效,这里返回pos的下一个位置,也就是next节点。

clear():

图
clear的作用就是只保留list的头结点,将其余节点全部释放掉。

准备工作做好以后就可以模拟实现了:

传统写法:

图

  • 如果是自己给自己赋值的话,直接返回自己本身就可以。
  • 它的本质就是被赋值一方只保留头结点,其他节点全部删除,然后再逐个尾插。
  • 使用范围for的时候,为了提高效率,使用的是auto&。

现代写法:

同样需要做一个准备工作,需要写一个拷贝构造函数,也正回补上了前面的遗漏。

拷贝构造函数:

图
使用范围for进行尾插。

图
还需要我们自己实现一个交换函数,只需要交换两个list的头结点就可以。

  • 算法库中的swap函数直接使用的话,会导致效率低下,因为算法库中是交换所有的内容。

图

  • 形参不能使用list,因为不能将this的_head换给原本提供数据的list对象。
  • 此时的val是一个临时变量,this中的_head和它交换以后,val会被释放。

😼析构函数

先来看析构函数的实现:

图

析构函数首先要做的是除了头结点以外的其他节点释放,最后再释放头结点,否则就会找不到其他节点。

🐵析构函数存在的必要性

图

节点类和迭代器类,就没有析构函数,这是为什么?他们不需要释放吗?

  • 对于迭代器,它的成员只有一个指针,所以就存放在栈区就可以,也不会占用太多的内存,不用在堆区开辟动态空间,所以不需要析构函数。
  • 对于节点类,它本身并不会创建类对象,而是list类会按照它的蓝图在堆区创建一个节点对象。所以说,它是由list类对象控制的,包括创建和释放,所以节点类本身不需要析构函数。

甚至他们仅有一个构造函数,都不用考虑深层次拷贝的问题,这是为什么?

  • 对于迭代器,例如语句iterator it = lt.begin(),它本质上使用的是一个拷贝构造函数,但是我们并没有写,这是因为编译器自动生成的默认拷贝构造函数就能满足要求,此时迭代器需要的就是完全复制lt.begin()的地址。
  • 对于节点类,对具体的节点操作,只有创建和销毁,还有访问,至于赋值什么的主要操作的是头结点,所以它没有必要进行深层次拷贝。

😼容量和访问操作

🐵容量操作

图
需要增加一个成员变量_size,用来记录节点个数。同样的,在一些增加和减少成员函数中,也要改变_size的值。

🐵访问操作

链表并不支持随机访问,只有访问头和尾的成员函数。

图
如上图所示,是访问头和尾的普通对象和const对象调用的成员函数。

图
上图是对应的运行结果。

😼增删查改操作

insert():

图
插入一个新的节点,本质上就是在修改新节点以及该位置前后俩个节点共三个节点的链接关系。每插入一个节点,数据个数要加1。为了防止迭代器实失效,最后返回的是新插入节点的位置。

图
此时push_back也可以进行复用,使用insert在末尾插入。

push_front():

tu
同样在复用insert。

图
成功头插了五个数字。

pop_back()和pop_front():

tu
这俩个函数只需要复用erase即可。

图

😼其他几个常用接口

sort():

list还提供了一个排序接口。

图
可以看到,将一组乱序的数字排成了有序。

unique():

tu
该函数的作用就是去重,将重复的数据去掉。

  • 去重之前必须先让它有序,所以要在unique之前使用sort来排序。

remove():

图
该函数的作用就是去除掉链表中指定的数据。

  • 指定数据如果有多个,就会有多少去除掉多少。
  • 指定数据如果没有,那么它什么也不会干,也不会报错。

merge():
图
将两个链表有序合并。

😼list和vector优劣比较

vector

优点:

  1. 支持下标随机访问。
  2. 尾插尾删效率高(偶尔扩容除外)。
  3. cpu高速缓存命中率高。

缺点:

  1. 前面部分插入删除效率低。
  2. 扩容有消耗,而且存在一定的空间浪费。

list

优点:

  1. 按需申请释放,不需要扩容。
  2. 支持任意位置插入删除,时间复杂度是O(1)。

缺点:

  1. 不支持下标随机访问。
  2. cpu高速缓存命中率低。

list和vector就像左右手的关系,它们的优劣势互补。一般vector满足的场景下优先使用vector。

😼总结

list中,需要我们重点掌握的就是它的迭代器,因为这里的迭代器和之前vector已经string的不同,它是封装好的一个类。进行list的模拟实现,主要目的是为了更好的了解和使用list,并不是为了造一个更好的轮子,官方提供的STL模板已经非常完美了。

相关文章:

【C++学习】list的使用及模拟实现

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《C学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; list的使用及模拟实现&#x1f63c;构造函数&#x1f435;模拟实现&#x1f63c;迭代器&#x1f435;…...

动态规划专题精讲1

致前行的人&#xff1a; 要努力&#xff0c;但不要着急&#xff0c;繁花锦簇&#xff0c;硕果累累都需要过程&#xff01; 前言&#xff1a; 本篇文章为大家带来一种重要的算法题&#xff0c;就是动态规划类型相关的题目&#xff0c;动态规划类的题目在笔试和面试中是考察非常高…...

PPO(proximal policy optimization)算法

博客写到一半发现有篇讲的很清楚&#xff0c;直接化缘了 https://www.jianshu.com/p/9f113adc0c50 Policy gradient 强化学习的目标&#xff1a;学习到一个策略πθ(a∣s)\pi\theta(a|s)πθ(a∣s)来最大化期望回报。 一种直接的方法就是在策略空间中直接搜索来得到最优策略&…...

ElasticSearch基本使用

title: ElasticSearch基本使用 date: 2022-08-29 00:00:00 tags: ElasticSearch基本使用 categories:ElasticSearch 基本概念 随着ES版本的升级&#xff0c;文中有些概念可能已经废弃。 索引词(term) 一个能够被索引的精确值&#xff0c;区分大小写&#xff0c;可以通过term查…...

windows微软商店下载应用失败/下载故障的解决办法;如何在网页上下载微软商店的应用

一、问题背景 设置惠普打印机时&#xff0c;需要安装hp smart&#xff0c;但是官方只提供微软商店这一下载渠道。 点击安装HP Smart&#xff0c;确定进入微软商店下载。 完全加载不出来&#xff0c;可能是因为开了代理。 把代理关了&#xff0c;就能正常打开了。 但是点击“…...

MySQL进阶篇之InnoDB存储引擎

06、InnoDB引擎 6.1、逻辑存储结构 表空间&#xff08;Tablespace&#xff09; 表空间在MySQL中最终会生成ibd文件&#xff0c;一个mysql实例可以对应多个表空间&#xff0c;用于存储记录、索引等数据。 段&#xff08;Segment&#xff09; 段&#xff0c;分为数据段&#x…...

商标侵权行为的种类有哪些

商标侵权行为的种类有哪些 1、商标侵权行为的种类有以下七种&#xff1a; (1)未经商标注册人的许可&#xff0c;在同一种商品上使用与其注册商标相同的商标的; (2)未经商标注册人的许可&#xff0c;在同一种商品上使用与其注册商标近似的商标&#xff0c;或者在类似商品上使…...

Similarity-Preserving KD(ICCV 2019)原理与代码解析

paper&#xff1a;Similarity-Preserving Knowledge Distillationcode&#xff1a;https://github.com/megvii-research/mdistiller/blob/master/mdistiller/distillers/SP.py背景本文的灵感来源于作者观察到在一个训练好的网络中&#xff0c;语义上相似的输入倾向于引起相似的…...

在Linux和Windows上安装seata-1.6.0

记录&#xff1a;381场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;安装seata-1.6.0。在Windows上操作系统上&#xff0c;安装seata-1.6.0。Seata&#xff0c;一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。版本&#xff1a;JDK…...

兼职任务平台收集(二)分享给有需要的朋友们

互联网时代&#xff0c;给人们带来了很大的便利。信息交流、生活缴费、足不出户购物、便捷出行、线上医疗、线上教育等等很多。可以说&#xff0c;网络的时代会一直存在着。很多人也在互联网上赚到了第一桶金&#xff0c;这跟他们的努力和付出是息息相关的。所谓一份耕耘&#…...

目标检测三大数据格式VOC,YOLO,COCO的详细介绍

注&#xff1a;本文仅供学习&#xff0c;未经同意请勿转载 说明&#xff1a;该博客来源于xiaobai_Ry:2020年3月笔记 对应的PDF下载链接在&#xff1a;待上传 目录 目标检测常见数据集总结 V0C数据集(Annotation的格式是xmI) A. 数据集包含种类: B. V0C2007和V0C2012的区别…...

SpringBoot实现统一返回接口(除AOP)

起因 关于使用AOP去实现统一返回接口在之前的博客中我们已经实现了&#xff0c;但我突然突发奇想&#xff0c;SpringBoot中异常类的统一返回好像是通过RestControllerAdvice 这个注解去完成的&#xff0c;那我是否也可以通过这个注解去实现统一返回接口。 正文 这个方法主要…...

ChatGpt - 基于人工智能检索进行论文写作

摘要 ChatGPT 是一款由 OpenAI 训练的大型语言模型,可用于各种自然语言处理任务,包括论文写作。使用 ChatGPT 可以帮助作者提高论文的语言流畅度、增强表达能力和提高文章质量。在写作过程中,作者可以使用 ChatGPT 生成自然语言的段落、句子、单词或者短语,作为启发式的写…...

实例三:MATLAB APP design-多项式函数拟合

一、APP 界面设计展示 注:在左侧点击数据导入,选择自己的数据表,如果数据导入成功,在右侧的空白框就会显示数据导入成功。在多项式项数右侧框中输入项数,例如2、3、4等,点击计算按钮,右侧坐标框就会显示函数图像,在平均相对误差下面的空白框显示平均相对误差。...

springboot多种方式注入bean获取Bean

springboot动态注入bean1、创建Bean(demo)2、动态注入Bean3、通过注解注入Bean4、通过config配置注入Bean5、通过Import注解导入6、使用FactoryBean接口7、实现BeanDefinitionRegistryPostProcessor接口1、创建Bean(demo) Data public class Demo(){private String name;publi…...

Markdown及其语法详细介绍(全面)

文章目录一、基本语法1.标题2.段落和换行3.强调4.列表5.链接6.图片7.引用8.代码9.分割线10表格二、扩展语法1.标题锚点标题 {#anchor}2.脚注3.自动链接4.任务列表5.删除线6.表情符号7.数学公式三、Markdown 应用1.文档编辑2.博客写作3.代码笔记四、常见的工具和平台支持 Markdo…...

在Linux和Windows上安装sentinel-1.8.5

记录&#xff1a;380场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;安装sentinel-1.8.5。在Windows上操作系统上&#xff0c;安装sentinel-1.8.5。Sentinel是面向分布式、多语言异构化服务架构的流量治理组件。版本&#xff1a;JDK 1.8 sentinel-1.8.5 CentOS 7.9官网地址…...

面试攻略,Java 基础面试 100 问(十)

StringBuffer、StringBuilder、String区别 线程安全 StringBuffer&#xff1a;线程安全&#xff0c;StringBuilder&#xff1a;线程不安全。 因为 StringBuffer 的所有公开方法都是 synchronized 修饰的&#xff0c;而 StringBuilder 并没有 synchronized 修饰。 StringBuf…...

Zero-shot(零次学习)简介

zero-shot基本概念 首先通过一个例子来引入zero-shot的概念。假设我们已知驴子和马的形态特征&#xff0c;又已知老虎和鬣狗都是又相间条纹的动物&#xff0c;熊猫和企鹅是黑白相间的动物&#xff0c;再次的基础上&#xff0c;我们定义斑马是黑白条纹相间的马科动物。不看任何斑…...

51单片机简易电阻电感电容RLC测量仪仿真设计

51单片机简易电阻电感电容RLC测量仪仿真( proteus仿真程序讲解视频&#xff09; 仿真图proteus7.8及以上 程序编译器&#xff1a;keil 4/keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;S0040 51单片机简易电阻电感电容RLC测量仪仿真51单片机最小系统的相关知识复位…...

[软件工程导论(第六版)]第6章 详细设计(课后习题详解)

文章目录1 假设只有SEQUENCE和DO-WHILE两种控制结构&#xff0c;怎样利用它们完成 IF THEN ELSE操作&#xff1f;2 假设只允许使用SEQUENCE和IF-THEN-ELSE两种控制结构&#xff0c;怎样利用它们完成DO WHILE操作&#xff1f;3 画出下列伪码程序的程序流程图和盒图&#xff1a;4…...

【2.19】算法题2:贪心算法、动态规划、分治

题目&#xff1a;给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。子数组 是数组中的一个连续部分。方法一&#xff1a;贪心算法原理&#xff1a;若当前指针所指元素之前的和小…...

【Redis】Redis 发布订阅通信模式 ( 发布订阅模式 | 订阅频道 | 发布消息 | 接收消息 )

文章目录一、发布订阅模式二、订阅频道三、发布消息四、接收消息一、发布订阅模式 Redis 中 存在一种 发布订阅 消息通信模式 : 消息发布者 : 负责发送消息 , 订阅者需要订阅该发布者频道 ;消息订阅者 : 负责接收消息 ; 订阅者 先 订阅 发布者频道 , 当 发布者 发布消息时 , …...

VNCTF 2023复现

文章目录象棋王子电子木鱼BabyGo象棋王子 签到题&#xff0c;直接在源码中找就ok。 找到一处编码&#xff0c;在控制台输出。 flag为&#xff1a;flag{w3lc0m3_t0_VNCTF_2023~~~} 电子木鱼 需要先理清代码逻辑。 存在三个路由。 一&#xff1a;/路由用来查看当前的功德数量…...

python基础知识有哪些需要背(记住是基础知识)我是初学者

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;一个有趣的事情&#xff0c;一个有趣的事情&#xff0c;今天让我们一起来看看吧&#xff01; 1、python基础知识有哪些需要背&#xff08;记住是基础知识&#xff09;我是初学者 或看好Python的广阔前景&#xff0c;或…...

Linux下TCP连接断开后不释放的解决办法

问题&#xff1a;在开发测试时发现断开与服务器端口后再次连接时拒绝连接。 分析&#xff1a;服务器上查看端口占用情况&#xff0c;假设端口为8888。 netstat -anp |grep 8888 发现端口8888端口显示被占用&#xff08;ip为本机ip确定是上次连接&#xff09;且状态为ESTABLI…...

1.关于嵌入式开发软件工程师的理解

学习嵌入式软件开发&#xff0c;首先要学会使用工具&#xff0c; 包括各种语言&#xff0c;C语言、FPGA、C等各种工具软件&#xff0c;各种芯片开发的IDE环境各种操作系统&#xff0c;Vxworks、Linux、Freertos等计算机基础&#xff0c;基本的框架结构&#xff0c;网络通信等编…...

1760字,让你拿捏 [‘列表‘]

如约而至&#xff0c;紧接着第一篇文章&#xff0c;小编将会陆续把自己精心做的全套Python笔记依次发放给大家&#xff0c;便于大家学习Python、期末备考、巩固基础等(这几期是公众号小插曲&#xff0c;后期发放编程技术的话主要还是会围绕Java来展开&#xff0c;感谢小伙伴们的…...

A562基于android的养老APP

需求信息&#xff1a; 1&#xff1a;家庭信息管理,包括家庭成员基本情况、性别、年龄、关系、工作单位、联系方式&#xff08;手机号码、微信等&#xff09;&#xff1b; 2&#xff1a;个人健康数据管理,包括姓名、性别、年龄、关系、原工作单位、联系方式&#xff08;手机号码…...

java面试题-并发基础

1.多线程的出现是要解决什么问题的? 本质什么?提高程序性能&#xff1a;单线程程序只能按照固定的顺序依次执行每个任务&#xff0c;无法同时处理多个任务。多线程技术可以在同一时间内执行多个任务&#xff0c;从而提高程序的运行效率和响应速度。提高程序的并发性&#xff…...

网络营销推广方法哪家正规/武汉seo首页优化技巧

我在28岁生日那天电信一个副总劝我出来开公司算了&#xff0c;然后想了一天第二天就去工商局注册了&#xff0c;从有开公司的想法到工商局注册仅仅一天时间! 然后2003年8 月份拿到营业执照&#xff0c;根本不知道安全是怎么一回事情&#xff0c;找北大方正一个技术副总和我讲了…...

华讯网络工程师待遇/windows优化大师是什么软件

你可以用v-model指令在表单<input>、<textarea>及<select>元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇&#xff0c;但v-model本质上不过是语法糖。它负责监听用户的输入事件以更新数据&#xff0c;并对一些极端场景…...

如何查询网站备案信息/中囯联通腾迅

本月合计、本年累计...

asp网站报错信息/西安seo排名收费

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全生产模拟考试一点通&#xff1a;安全员-C证考试内容根据新安全员-C证考试大纲要求&#xff0c;安全生产模拟考试一点通将安全员-C证模拟考试试题进行汇编&#xff0c;组成一套安全员-C证全真模拟考试试题&#xf…...

网站用图片做背景图片/石家庄百度搜索引擎优化

C语言简介&#xff1a;BCPL->newB->C->UNIX->Linux->gccC语言专门编写操作系统的语言&#xff0c;所以天生适合对硬件编程&#xff0c;也以运行速度快著称&#xff0c;也非常适合实现数据结构和算法。由于出现时间过早&#xff0c;有很多缺陷&#xff0c;也没想…...

用vs与dw做网站/自媒体培训学校

内容介绍&#xff0c;为什么要使用前端路由&#xff1f; 在2005左右&#xff0c;兴起了一种叫做ajax的技术&#xff0c;有了ajax之后&#xff0c;我们向服务端提交数据的时候就不再需要使用from表单去提交了&#xff0c;因为from表单之间的提交会导致页面之间的切换&#xff0c…...