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

c++----list模拟实现

目录

1. list的基本介绍

2. list的基本使用 

2.1 list的构造

用法示例 

2.2 list迭代器 

 用法示例

2.3. list容量(capacity)与访问(access)

用法示例

2.4 list modifiers 

用法示例 

2.5 list的迭代器失效 

3.list的模拟实现 

3.1 构造、析构

3.2 迭代器 

3.3 list modifiers 

4. list与vector的区别 

5.完整代码 


1. list的基本介绍

1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2. list的底层是双向带头循环链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝后迭代。
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

下图是list的底层结构。

从图中可以看出,list底层由一个双向带头循环链表构成。假设有一链表list<int> lt, 头节点为_head则lt.begin()指向_head->next, lt.end()指向_head。

2. list的基本使用 

2.1 list的构造

构造函数( (constructor))接口说明
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
list()构造空的list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list

用法示例 

void list_test1()
{list<string> lt(5, "xy");//n val构造for (auto e : lt){cout << e << " ";}cout << endl;list<string> llt(lt);//拷贝构造for (auto e : llt){cout << e << " ";}cout << endl;list<string> lltt(++lt.begin(), --lt.end());//迭代器区间构造for (auto e : lltt){cout << e << " ";}
}

2.2 list迭代器 

函数声明接口说明
begin +
end
返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin +
rend
返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的
reverse_iterator,即begin位置

 用法示例

void list_test2()
{list<int> lt{ 1,2,3,4,5};//这样构造很奇特for (auto e : lt){cout << e << " ";}cout << endl;list<int>::iterator it = lt.begin();//正向迭代器while (it != lt.end()){cout << *it << " ";it++;}cout << endl;list<int>::reverse_iterator  rit = lt.rbegin();//反向迭代器while (rit != lt.rend()){cout << *rit << " ";rit++;}cout << endl;
}

那么大家肯定会有一个疑问,之前的string,vector迭代器可以++,是因为他们的底层物理空间是连续的,但list底层可是双向带头循环链表啊,它的结构物理空间可是不连续的,我们++,不就错了吗?

不急,我们后面模拟实现的时候细讲。

2.3. list容量(capacity)与访问(access)

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size

返回list中有效节点的个数

front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

用法示例

void list_test3()
{list<int> lt{ 1,2,3,4,5 };//这样构造很奇特for (auto e : lt){cout << e << " ";}cout << endl;cout << "链表是否为空:   ";if (lt.empty())cout << "true" << endl;elsecout << "false" << endl;cout << "size: " << lt.size() << endl;cout << "Front Element: " << lt.front() << endl;cout << "Back Element: " << lt.back() << endl;lt.clear();cout << "已执行链表清空操作,链表是否为空:   ";if (lt.empty())cout << "true" << endl;elsecout << "false" << endl;}

2.4 list modifiers 

函数声明接口说明
push_front在list首元素前插入值为val的元素
pop_front删除list中第一个元素
push_back在list尾部插入值为val的元素
pop_back删除list中最后一个元素
insert在list position 位置中插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素

用法示例 

void list_test4()
{list<int> lt{ 1,2,3,4,5 };//这样构造很奇特for (auto e : lt){cout << e << " ";}cout << endl;lt.push_back(0);lt.push_back(0);lt.push_front(9);lt.push_front(9);for (auto e : lt){cout << e << " ";}cout << endl;lt.pop_back();lt.pop_front();for (auto e : lt){cout << e << " ";}cout << endl;list<int>::iterator it = lt.begin();while (it != lt.end()){if (*it == 5){it = lt.erase(it);break;}it++;}lt.insert(it, 999);for (auto e : lt){cout << e << " ";}cout << endl;
}

2.5 list的迭代器失效 

这里我们将迭代器理解为指针,list的迭代器失效是因为指针所指向的空间被销毁,导致指针变为野指针。list的底层是双向带头循环链表,因此list不存在插入导致迭代器失效的问题。list的迭代器失效出现在erase内,如果该节点被删除,空间已被销毁,那么就必须更新迭代器,否则迭代器失效。 

3.list的模拟实现 

list的模拟实现存在几个难点,首先是使用了大量的typedef,这会让人头晕眼花,其次是模板迭代器的构建。我们快来看看吧。

注意:list的迭代器由于list底层空间不连续的问题,因此要实现和vector一样的迭代器效果(即支持*it,it++)需要对操作符进行重载,所以要封装Node*。

list需要创建一个类内类Node,用list访问Node类的变量。

下面是ListNode类,类内包含双链表节点的三要素。

template<class T>
struct ListNode//节点的创建
{T _Date;ListNode<T>* _next;ListNode<T>* _prev;ListNode(const T& x=T())//节点的构造函数:_Date(x),_next(nullptr),_prev(nullptr){}
};

3.1 构造、析构

这里的构造函数为简易版本,只支持创建空链表。 

void list_init()//初始化每一个节点
{_head = new Node;//new调用Node的默认构造,不传参_head->_next = _head;_head->_prev = _head;
}
list()
{list_init();
}
list(list<T>& lt)//拷贝构造
{list_init();//创建头节点for (const auto& e : lt){push_back(e);}
}void swap(list<T>& tmp)
{std::swap(_head, tmp._head);
}//list& operator=(list lt)
list<T>& operator=(list<T> lt)
{swap(lt);return *this;
}
void clear()
{list<T>::iterator it = this->begin();while (it != end()){it = erase(it);}
}
~list()
{delete _head;_head = nullptr;
}

3.2 迭代器 

前面说,要对++等操作符进行重载,这时需要重新定义iterator类封装节点的指针,通过指针对节点进行操作。且iterator类需要支持普通对象与const对象的访问,因此需要定义成模板类。

模板类iterator 

template<class T,class Ref,class Ptr>
struct _list_iterator//迭代器本质就是指针,这里封装Node*,对Node的指针进行操作,重载++等操作符
{typedef ListNode<T> Node;typedef _list_iterator<T,Ref,Ptr> self;//typedef _list_iterator<T> self;Node* _node;//成员变量public:_list_iterator(Node* node)//构造函数,直接用node构建:_node(node){}//没有参数int ,前置++self& operator++()//重载++{_node = _node->_next;return *this;}self operator++(int)//后置++{Node* tmp = _node;_node = _node->_next;return tmp;}self& operator--(){_node = _node->_prev;return *this;}//返回临时对象,出函数即销毁,不可传引用self operator--(int)//后置--{Node* tmp = _node;_node = _node->_prev;return tmp;}Ptr operator->(){return &_node->_Date;}Ref operator*(){return _node->_Date;}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}
};

list类内的迭代器函数 

typedef _list_iterator<T,T&,T*> iterator; //普通对象的迭代器
typedef _list_iterator<T,const T&,const T*> const_iterator;  //const对象的迭代器
iterator begin()//指向头节点的下一个
{return _head->_next;
}iterator end()//指向头节点
{return _head;
}const_iterator begin() const//指向头节点的下一个
{return _head->_next;
}const_iterator end() const//指向头节点
{return _head;
}

 这里模板参数有三个,分别是T,T&,T*,这是为什么呢?

T&:因为重载普通迭代器的*时,返回值需要设为引用类型,以便于外界可以更改。

 T*:重载->时,由于list的->逻辑不能自洽,list的重载->需要返回数据的地址

这里需要着重解释一下,如果是list<int>,那么重载->完全没有问题,但如果是list<Date>呢?

Node的数据域是自定义类型,又该怎么办呢?

因此我们返回Node.Date的地址,然后再解引用,如果是内置类型不受影响,如果是自定义类型,稍加控制就ok了。

我们来看看示例:

struct AA
{int _a1;int _a2;AA(int a1 = 1, int a2 = 1):_a1(a1), _a2(a2){}
};
void test_list5()
{list<AA> lt;AA aa1;lt.push_back(aa1);lt.push_back(AA());AA aa2(2, 2);lt.push_back(aa2);lt.push_back(AA(2, 2));list<AA>::iterator it = lt.begin();while (it != lt.end()){std::cout << (*it)._a1 << ":" << (*it)._a2 << std::endl;std::cout << it.operator*()._a1 << ":" << it.operator*()._a2 << std::endl;std::cout << it->_a1 << ":" << it->_a2 << std::endl;std::cout << it.operator->()->_a1 << ":" << it.operator->()->_a2 << std::endl;++it;}std::cout << std::endl;
}

Node的数据域存放的是AA类对象,Node* ptr,ptr->Date就是AA类的实例化对象,此时返回AA对象的地址,也就是AA* p,p->就可以访问AA类对象的成员了。 

3.3 list modifiers 

这里需要注意erase的迭代器失效问题。 

void push_back(const T& x=T())
{Node* node = new Node;Node* tail = _head->_prev;node->_Date = x;tail->_next = node;node->_next = _head;_head->_prev = node;node->_prev = tail;
}iterator insert(iterator pos,const T& x = T())//不存在迭代器失效问题
{Node* node = new Node;node->_Date = x;Node* cur = pos._node;Node* prev = cur->_prev;cur->_prev = node;node->_next = cur;node->_prev = prev;prev->_next = node;return node;
}iterator erase(iterator pos)//这里的pos有可能会产生迭代器失效问题
{Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;cur = nullptr;return next;//更新pos
}

4. list与vector的区别 

list与vector都是c++STL序列容器的重要组成部分,由于底层结构的不同,造成了他们的功能和特性有所不同,详见下表。 

vectorlist
底 层 结 构动态顺序表,一段连续空间带头结点的双向循环链表
随 机 访 问支持随机访问,访问某个元素效率O(1)不支持随机访问,访问某个元素
效率O(N)
插 入 和 删 除任意位置插入和删除效率低,需要搬移元素,时间复杂
度为O(N),插入时有可能需要增容,增容:开辟新空
间,拷贝元素,释放旧空间,导致效率更低
任意位置插入和删除效率高,不
需要搬移元素,时间复杂度为
O(1)
空 间 利 用 率底层为连续空间,不容易造成内存碎片,空间利用率
高,缓存利用率高
底层节点动态开辟,小节点容易
造成内存碎片,空间利用率低,
缓存利用率低
迭 代 器原生态指针对原生态指针(节点指针)进行封装
迭 代 器 失 效在插入元素时,要给所有的迭代器重新赋值,因为插入
元素有可能会导致重新扩容,致使原来迭代器失效,删
除时,当前迭代器需要重新赋值否则会失效
插入元素不会导致迭代器失效,
删除元素时,只会导致当前迭代
器失效,其他迭代器不受影响
使 用 场 景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心随
机访问


 

5.完整代码 

template<class T>
struct ListNode//节点的创建
{T _Date;ListNode<T>* _next;ListNode<T>* _prev;ListNode(const T& x=T())//节点的构造函数:_Date(x),_next(nullptr),_prev(nullptr){}
};template<class T,class Ref,class Ptr>
struct _list_iterator//迭代器本质就是指针,这里封装Node*,对Node的指针进行操作,重载++等操作符
{typedef ListNode<T> Node;typedef _list_iterator<T,Ref,Ptr> self;//typedef _list_iterator<T> self;Node* _node;//成员变量public:_list_iterator(Node* node)//构造函数,直接用node构建:_node(node){}//没有参数int ,前置++self& operator++()//重载++{_node = _node->_next;return *this;}self operator++(int)//后置++{Node* tmp = _node;_node = _node->_next;return tmp;}self& operator--(){_node = _node->_prev;return *this;}//返回临时对象,出函数即销毁,不可传引用self operator--(int)//后置--{Node* tmp = _node;_node = _node->_prev;return tmp;}Ptr operator->(){return &_node->_Date;}Ref operator*(){return _node->_Date;}bool operator!=(const self& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}
};template<class T>
class list
{typedef ListNode<T> Node;  //typedef
public:typedef _list_iterator<T,T&,T*> iterator; //普通对象的迭代器typedef _list_iterator<T,const T&,const T*> const_iterator;  //const对象的迭代器iterator begin()//指向头节点的下一个{return _head->_next;}iterator end()//指向头节点{return _head;}const_iterator begin() const//指向头节点的下一个{return _head->_next;}const_iterator end() const//指向头节点{return _head;}void list_init()//初始化每一个节点{_head = new Node;//new调用Node的默认构造,不传参_head->_next = _head;_head->_prev = _head;}list(){list_init();}list(list<T>& lt)//拷贝构造{list_init();//创建头节点for (const auto& e : lt){push_back(e);}}void swap(list<T>& tmp){std::swap(_head, tmp._head);}//list& operator=(list lt)list<T>& operator=(list<T> lt){swap(lt);return *this;}void clear(){list<T>::iterator it = this->begin();while (it != end()){it = erase(it);}}~list(){delete _head;_head = nullptr;}void push_back(const T& x=T()){Node* node = new Node;Node* tail = _head->_prev;node->_Date = x;tail->_next = node;node->_next = _head;_head->_prev = node;node->_prev = tail;}iterator insert(iterator pos,const T& x = T())//不存在迭代器失效问题{Node* node = new Node;node->_Date = x;Node* cur = pos._node;Node* prev = cur->_prev;cur->_prev = node;node->_next = cur;node->_prev = prev;prev->_next = node;return node;}iterator erase(iterator pos)//这里的pos有可能会产生迭代器失效问题{Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;cur = nullptr;return next;//更新pos}size_t size()const//返回size{int cnt = 0;list<int>::const_iterator it = begin();while (it != end()){cnt++;it++;};return cnt;}bool empty()const//判空{return _head ->_next== _head;}private:Node* _head;//成员变量为ListNode* 即访问节点的指针
};


 

相关文章:

c++----list模拟实现

目录 1. list的基本介绍 2. list的基本使用 2.1 list的构造 用法示例 2.2 list迭代器 用法示例 2.3. list容量&#xff08;capacity&#xff09;与访问&#xff08;access) 用法示例 2.4 list modifiers 用法示例 2.5 list的迭代器失效 3.list的模拟实现 3.1…...

FastAPI+React全栈开发15 让我们构建一个展示API

Chapter03 Getting Started with FastAPI 15 Let’s Build a showcase API FastAPIReact全栈开发15 让我们构建一个展示API REST APIs are all about cycles of HTTP requests and responses, it is the engine that powers the web and is implemented in every web framew…...

list(链表)容器(二)

一、list 插入和删除 函数原型&#xff1a; push_back(elem);//在容器尾部加入一个元素 pop_back();//删除容器中最后一个元素 push_front(elem);//在容器开头插入一个元素 pop_front();//从容器开头移除第一个元素 insert(pos,elem);//在pos位置插elem元素的拷贝&#xff0c…...

世优科技上榜2024年度《中国虚拟数字人影响力指数报告》

日前&#xff0c;第三期《中国虚拟数字人影响力指数报告》在中国网络视听大会上正式发布。本期《报告》由中国传媒大学媒体融合与传播国家重点实验室&#xff08;以下简称“国重实验室”&#xff09;、中国传媒大学数字人研究院编制&#xff0c;中国网络视听协会、人民日报智慧…...

【调试方法】C代码中dump中间数据的方法

一&#xff0c;简介 本文主要介绍&#xff0c;如何在C语言代码中将音频流数据进行写入文件&#xff0c;方便调试定位问题&#xff1a; 二&#xff0c;函数实现 按int8_t写入 #include <stdio.h>int32_t write_int8_t_data(int8_t *name, int8_t *buffer, int32_t dat…...

【BUG】vue中@change时间传值丢失问题

项目场景&#xff1a; 在修改项目bug时&#xff0c;发现后端响应到前端的值&#xff0c;通过change事件调用方法&#xff0c;在方法中拿到值时&#xff0c;有部分数据丢失。 问题描述 后端传到前端的值为&#xff1a;字符串类型的"00000089"&#xff0c;change调用…...

Linux提权!!!

上一篇文章讲了Windows的提权&#xff0c;那么这篇文章就来讲一下Linux的提权 1.SUID提权 suid权限 作用&#xff1a;让普通用户临时拥有该文件的属主的执行权限&#xff0c;suid权限只能应用在二进制可执行文件&#xff08;命令&#xff09;上&#xff0c;而且suid权限只能设置…...

Android Studio学习7——常用控件view

Android控件 双击shift键——>搜索想要找的文件 Ctrlshift回车——>补全“&#xff1b;”号 CtrlX——>删除一行&#xff0c;只需把鼠标放在那一行 windows自带字体...

Springboot3 集成knife4j(swagger)

knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍! 官网地址&#xff1a; Knife4j 集Swagger2及OpenAPI3为一体的增强解决方案. | Knife4j 本文以Springboot3版本集成kn…...

深信服:借助观测云实现全链路可观测性

导读 深信服科技股份有限公司 简称「深信服」&#xff08; Sangfor Technologies Inc. &#xff09;&#xff0c;是一家领先的网络安全和云计算解决方案提供商&#xff0c;致力于为全球客户提供高效、智能、安全的网络和云服务。随着公司业务的不断扩展&#xff0c;也面临着监…...

详解Qt中使用线程

详解Qt中使用线程 Qt中的线程相关知识涵盖了线程创建、管理、通信以及线程安全等方面。下面将详细讲解这些知识点&#xff0c;并提供对应的示例代码。 线程创建与管理 QThread类 Qt通过QThread类来创建和管理线程。要创建一个新的工作线程&#xff0c;通常有两种方法&#…...

在.Net6中用gdal实现第一个功能

目录 一、创建.NET6的控制台应用程序 二、加载Gdal插件 三、编写程序 一、创建.NET6的控制台应用程序 二、加载Gdal插件 Gdal的资源可以经过NuGet包引入。右键单击项目名称&#xff0c;然后选择 "Manage NuGet Packages"&#xff08;管理 NuGet 包&#xff09;。N…...

采用大语言模型进行查询重写——Query Rewriting via Large Language Models

文章&#xff1a;Query Rewriting via Large Language Models&#xff0c;https://arxiv.org/abs/2403.09060 摘要 查询重写是在将查询传递给查询优化器之前处理编写不良的查询的最有效技术之一。 手动重写不可扩展&#xff0c;因为它容易出错并且需要深厚的专业知识。 类似地…...

使用Vue实现CSS过渡和动画

01-初识动画和过渡 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>使用vue实现css过渡和动画&l…...

一家购物商场的数据运营挑战

✅作者简介&#xff1a;《数据运营&#xff1a;数据分析模型撬动新零售实战》作者、《数据实践之美》作者、数据科技公司创始人、多次参加国家级大数据行业标准研讨及制定、高端企培合作讲师。 &#x1f338;公众号&#xff1a;风姑娘的数字视角&#xff0c;免费分享数据应用相…...

React Native框架开发APP,安装免费的图标库(react-native-vector-icons)并使用详解

一、安装图标库 要使用免费的图标库&#xff0c;你可以使用 React Native Vector Icons 库。 首先&#xff0c;确保你已经安装了 react-native-vector-icons&#xff1a; npm install --save react-native-vector-iconsnpm install --save-dev types/react-native-vector-ic…...

idea端口占用

报错&#xff1a;Verify the connector‘s configuration, identify and stop any process that‘s listening on port XXXX 翻译&#xff1a; 原因&#xff1a; 解决&#xff1a; 一、重启大法 二、手动关闭 启动spring项目是控制台报错&#xff0c;详细信息如下&#xff…...

MQ消息队列详解以及MQ重复消费问题

MQ消息队列详解以及MQ重复消费问题 1、解耦2、异步调用3、流量削峰4、MQ重复消费问题&#xff0c;以及怎么解决&#xff1f;4.1、重复消费产生4.2、解决方法&#xff1a; https://blog.csdn.net/qq_44240587/article/details/104630567 核心的就是&#xff1a;解耦、异步、削锋…...

系统IO函数接口

目录 前言 一. man手册 1.1 man手册如何查询 1.2 man手册基础 二.系统IO函数接口 三.open打开文件夹 3.1 例1 open打开文件 3.2 open打开文件代码 3.3 例2 创建文件 四.write写文件 4.1 write写文件 五. read读文件 5.1 read读文件与偏移 5.2 偏移细节 5.3 read读文件代码 六.复…...

06 监听器

文章目录 SessionAttListenerDemo.javaSessionListenerDemo.javaProductController.java SessionAttListenerDemo.java package com.aistart.listener;import javax.servlet.ServletContext; import javax.servlet.annotation.WebListener; import javax.servlet.http.HttpSess…...

C语言第三十九弹---预处理(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 预处理 1、预定义符号 2、#define定义常量 3、#define定义宏 4、带有副作用的宏参数 5、宏替换的规则 6、宏和函数的对比 总结 在C语言中&#xff0c;预处…...

计算机视觉无人驾驶技术:入门指南

I. 引言&#xff1a; 计算机视觉无人驾驶技术是一种基于计算机视觉和机器学习技术的自动化驾驶技术。它可以通过搭载各种传感器和摄像机&#xff0c;让车辆自主感知周围环境&#xff0c;实现尽可能自动化的驾驶操作。 这种技术具有重要性和优势&#xff0c;包括&#xff1a; …...

Golang和Java对比

其实我是Javaer转的Golang&#xff0c;我谈谈自己对Java和Golang的体会 我先讲讲我认为Golang的优点 1、Golang是一门新语言&#xff0c;相比于Java&#xff0c;他的生态要小很多&#xff0c;优点很明显&#xff0c;自由度高&#xff0c;学习成本低&#xff0c;能快速拉起一个…...

2024.2.29力扣每日一题——统计可能的树根数目

2024.2.29 题目来源我的题解方法一 深度搜索&#xff08;暴力&#xff09; 超时方法二 树形动态规划 题目来源 力扣每日一题&#xff1b;题序&#xff1a;2581 我的题解 方法一 深度搜索&#xff08;暴力&#xff09; 超时 以每个节点node为跟进行深度搜索&#xff0c;并在搜…...

同一个主机配置多个SSH key

使用git时&#xff0c;我们可能一个git客户端使用多个git服务器&#xff0c;比如github&#xff0c;自建gitlab&#xff0c;gitee&#xff0c;为了防止提交混乱&#xff0c;所以需要一一对应生成公私钥。 第一步&#xff1a; 使用ssh-keygen生成多对密钥对&#xff0c;比如&…...

SAP系统财务模块简介:实现财务管理的卓越之道

作为全球领先的企业管理软件提供商&#xff0c;SAP公司开发了一系列强大而全面的财务模块&#xff0c;帮助企业实现财务管理的高效运作与优化。SAP系统的财务模块涵盖了财务核算、成本管理、资金管理、资产会计等多个方面&#xff0c;为企业提供了完整的财务管理解决方案。本文…...

【pytest】功能特性及常用插件

pytest是一个功能强大的Python测试框架&#xff0c;它的语法简洁明了&#xff0c;易于学习和使用。同时&#xff0c;它提供了丰富的功能和插件&#xff0c;使得测试过程更加灵活和高效。 功能特性 pytest的主要功能特性包括&#xff1a; 参数化测试&#xff1a;允许使用不同…...

基于SpringBoot和Vue的房产销售系统的设计与实现

今天要和大家聊的是一款基于SpringBoot和Vue的房产销售系统的设计与实现 &#xff01;&#xff01;&#xff01; 有需要的小伙伴可以通过文章末尾名片咨询我哦&#xff01;&#xff01;&#xff01; &#x1f495;&#x1f495;作者&#xff1a;李同学 &#x1f495;&#x1f…...

ROS2从入门到精通1-2:详解ROS2服务通信机制与自定义服务

目录 0 专栏介绍1 服务通信模型2 服务模型实现(C)3 服务模型实现(Python)4 自定义服务5 话题、服务通信的异同 0 专栏介绍 本专栏旨在通过对ROS2的系统学习&#xff0c;掌握ROS2底层基本分布式原理&#xff0c;并具有机器人建模和应用ROS2进行实际项目的开发和调试的工程能力。…...

vue两个特性和什么是MVVM

一、什么是vue 1.构建用户界面 用vue往html页面中填充数据&#xff0c;非常的方便 2.框架 框架是一套线成的解决方案 vue的指令、组件&#xff08;是对ui结构的复用&#xff09;、路由、vuex 二、vue的特性 1.数据驱动视图 2.双向数据绑定 1.数据驱动视图 数据的变化会驱动…...

公司网站做地图地址/东莞网站推广方案

在老师的建议下&#xff0c;我利用假期两周的时间阅读了整本书&#xff0c;初读一次不解其中之味&#xff0c;当我读完第二遍的时候&#xff0c;我才品味出蒋涛老师为什么称《大道至简》闪烁着独立思考的光芒。 蒋涛老师在序言中提到“虽千化万端&#xff0c;而理为一贯”在学习…...

学院网站建设的意义/公司网络推广服务

欠拟合和过拟合出现原因及解决方案参考文章&#xff1a; &#xff08;1&#xff09;欠拟合和过拟合出现原因及解决方案 &#xff08;2&#xff09;https://www.cnblogs.com/zhhfan/p/10476761.html 备忘一下。...

网站推广优化趋势/免费seo课程

1. 前言 Linux中的netfilter提供了一个防火墙框架&#xff0c;具有很好的扩展性&#xff0c;除了自带的模块之外&#xff0c;用户可以根据自己的需求定义新的防火墙模块加入其中&#xff0c;而编程过程也不是很复杂&#xff0c;只要依葫芦画瓢即可&#xff0c;可在原来的类似…...

wordpress全站pjax/百度公司招聘信息

持续更新中。。。 尝试用蓝牙hid协议 文章目录1.个人实践1.1 app在后台处于没有被系统杀死时&#xff0c;可以自动重连和发数据1.2 但在后台被系统杀死(没有被收到杀死)&#xff0c;当连接状态发生变化的时候&#xff0c;系统会唤醒app,扫描后会调用1.3 因为后台获取位置时&…...

做网站开发数据库怎么写/百度搜索量最大的关键词

最近学了图的遍历&#xff0c;关于其中的算法详情请见此处 关于深搜和广搜是非常重要的&#xff0c;对一些矩阵和图的遍历起到不错的作用。下面以一些题目来训练&#xff0c;其中每道题都尽量给出深搜和广搜的2种做法。 总结&#xff1a;深搜类似于递归&#xff08;毕竟需要回溯…...

物流网站开题报告/seo搜索引擎是什么

官方的说法是 MySQL 4.1 and up uses an authentication protocol based on a password hashing algorithm that is incompatible with that used by older clients. ..... 如果你升级mysql到4.1以上版本后遇到以上问题,请先确定你的mysql client 是4.1或者更高版本.(WINDOWS下…...