【C++】STL之list深度剖析及模拟实现
目录
前言
一、list 的使用
1、构造函数
2、迭代器
3、增删查改
4、其他函数使用
二、list 的模拟实现
1、节点的创建
2、push_back 和 push_front
3、普通迭代器
4、const 迭代器
5、增删查改(insert、erase、pop_back、pop_front)
6、构造函数和析构函数
6.1、默认构造
6.2、构造 n 个 val 的对象
6.3、拷贝构造
6.4、迭代器区间构造
6.5、 赋值运算符重载
6.6、析构函数
三、list 模拟实现源代码
四、list 的迭代器失效
五、list 和 vector的对比
前言
- list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素。
- list 与 forward_list 非常相似:最主要的不同在于 forward_list 是单链表,只能朝前迭代,已让其更简单高效。
- 与其他的序列式容器相比(array,vector,deque),list 通常在任意位置进行插入、移除元素的执行效率更好。
- 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问.
一、list 的使用
1、构造函数
| 构造函数 | 接口说明 |
| 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 |
int main()
{// 默认构造list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);// 拷贝构造list<int> lt2(lt);// 构造 n 个节点list<int> lt3(5, 1);// 迭代器区间构造list<int> lt4(lt.begin(), lt.end());return 0;
}
2、迭代器
| 函数声明 | 接口说明 |
| begin + end | 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器 |
| rbegin + rend | 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置 |
int main()
{int a[] = { 1,2,3,4,5,6,7,8,9 };list<int> lt(a, a + 9);auto it = lt.begin();while (it != lt.end()){cout << *it << " ";it++;}cout << endl;return 0;
}
迭代器一般是用来遍历和查找的;
而反向迭代器的使用是类似的,只不过调用的函数换成了 rbegin 和 rend 。
注意:反向迭代器的迭代使用的也是++。但迭代器区间一样是[rbegin, rend);
3、增删查改
| 函数声明 | 接口说明 |
| 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 中的有效元素 |
int main()
{vector<int> v = { 1,2,3,4,5,6,7,8,9 };list<int> lt(v.begin(), v.end());for (auto e : lt) cout << e << " ";cout << endl;lt.push_front(10);lt.push_back(20);for (auto e : lt) cout << e << " ";cout << endl;lt.pop_front();lt.pop_back();for (auto e : lt) cout << e << " ";cout << endl;auto pos = find(lt.begin(), lt.end(), 5);lt.insert(pos, 50);for (auto e : lt) cout << e << " ";cout << endl;pos = find(lt.begin(), lt.end(), 8);lt.erase(pos);for (auto e : lt) cout << e << " ";cout << endl;return 0;
}
4、其他函数使用
| 函数声明 | 接口说明 |
| empty | 检测 list 是否为空,是返回 true ,否则返回 false |
| size | 返回 list 中有效节点的个数 |
| front | 返回 list 的第一个节点中值的引用 |
| back | 返回 list 的最后一个节点中值的引用 |
二、list 的模拟实现
1、节点的创建
template<class T>
struct list_node//节点
{list_node<T>* _next;list_node<T>* _prev;T _data;// 构造函数list_node(const T& x = T()):_next(nullptr), _prev(nullptr), _data(x){}
};
由于节点存储的数据可能是任意类型,所以我们需要将将节点定义为模板类。这里我们需要写一个给缺省值的默认构造函数,便于之后在主类中new一个新节点时直接初始化,同时将两个指针置为空,将数据写入数据域中。
2、push_back 和 push_front
class list
{
public:typedef list_node<T> node;private:node* _head;
}
//尾插
void push_back(const T& x) const
{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;
}
//头插
void push_front(const T& x)
{node* head = _head->_next;node* new_node = new node(x);_head->_next = new_node;new_node->_prev = _head;new_node->_next = head;head->_prev = new_node;
}
这里模拟的头插和尾插也很简单,因为和我们之前在数据结构时候的双向循环链表是一样的,只需要找到头或者尾,然后链接四个节点间的关系即可。
3、普通迭代器
注意:list 的迭代器是自定义类型,不是原生指针node*。
迭代器为自定义类型,其中*,++等都是通过运算符重载来完成的。
所以我们需要重载的符号:*,->,前置++,后置++,前置--,后置--,!=,==;
template<class T>
struct __list_iterator
{typedef list_node<T> node;typedef __list_iterator<T> self;node* _node;//构造函数__list_iterator(node* n):_node(n){}//重载*运算符T& operator*(){return _node->_val;}T* 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& s){return _node != s._node;}//重载==运算符bool operator==(const self& s){return _node == s._node;}
};
此处我实现了一个简单的正向迭代器,使用一个模板参数T表示类型。
当普通迭代器封装好了之后,我们需要在list类中来实现它的 begin() 和 end() 方法。由于迭代器的名字一般都是 iterator,而且对于范围for来说,也只能通过 iterator 来转换为迭代器进行遍历。所以这里我们将其typedef为iterator。
template<class T>
class list//链表
{typedef list_node<T> node;
public:typedef __list_iterator<T> iterator;iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}
private:node* _head;
};
4、const 迭代器
const迭代器与普通迭代器的区别在于const迭代器指向的内容是不能修改的,但是它的指向是可以修改的。
template<class T>
class list//链表
{typedef list_node<T> node;
public:typedef __list_const_iterator<T> const_iterator;const_iterator begin(){return const_iterator(_head->_next);}const_iterator end(){return const_iterator(_head);}
private:node* _head;
};
我们最好的做法就是在__list_iterator 的类模板中的添加两个模板参数,然后再 list 类中 typedef 两份分别将第二个参数分别改成 T& 和 const T& 的类型,本质上就是让编译器根据传入的 Ref 的不同来自动示例化出 const 迭代器类,而我们还需要重载一个->运算符,因为list中可能存储的是自定义类型,这个自定义类型如果要是有多个成员变量的话,我们就需要使用->来解引用访问成员变量,同样还是要区分普通迭代器和const 迭代器,所以就增加了另一个模版参数 Ptr。具体的解决做法如下:
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* n):_node(n){}Ref operator*()//解引用{return _node->_data;}Ptr operator->(){return &_node->_data;}...
};
然后,最终在链表类中使用如下:
template<class T>
class list//链表
{typedef list_node<T> node;
public:typedef __list_iterator<T, T&, T*> iterator;//普通迭代器typedef __list_iterator<T, const T&, const T*> const_iterator;//const迭代器iterator begin(){return iterator(_head->_next);//匿名对象的返回}const_iterator begin() const{return const_iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator end() const{return const_iterator(_head);}
private:node* _head;
};
5、增删查改(insert、erase、pop_back、pop_front)
// 指定位置插入
void insert(iterator pos, const T& x)
{node* cur = pos._node;node* prev = cur->_prev;node* new_node = new node(x);prev->_next = new_node;new_node->_prev = prev;new_node->_next = cur;cur->_prev = new_node;
}
// 指定位置删除
iterator erase(iterator pos)
{assert(pos != end());node* prev = pos._node->_prev;node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;return iterator(next);
}
// 尾删
void pop_back()
{erase(--end());
}
// 头删
void pop_front()
{erase(begin());
}
6、构造函数和析构函数
6.1、默认构造
由于后面会频繁对空进行初始化,所以在这里对它进行了封装,方便后面的调用。
void empty_init()//空初始化
{_head = new node;_head->_next = _head;_head->_prev = _head;
}
list()
{empty_init();
}
6.2、构造 n 个 val 的对象
//用n个val构造对象
list(int n, const T& val = T())
{empty_init();for (int i = 0; i < n; i++){push_back(val);}
}
6.3、拷贝构造
//拷贝构造传统写法
list(const list<T>& lt)
{empty_init();for (auto e : lt){push_back(e);}
}
//拷贝构造现代写法
list(const list<T>& lt)
{empty_init();list<T> tmp(lt.begin(), lt.end());swap(tmp);
}
6.4、迭代器区间构造
template <class Iterator>
list(Iterator first, Iterator last)
{empty_init();while (first != last){push_back(*first);++first;}
}
6.5、 赋值运算符重载
//赋值运算符重载
list<T>& operator=(list<T> lt)//注意这里不能用引用
{swap(lt);return *this;
}
6.6、析构函数
//要全部清理掉
~list()
{clear();delete _head;_head = nullptr;
}
//不释放头结点
void clear()
{iterator it = begin();while (it != end()){it = erase(it);//这样也可以//erase(it++);}
}
三、list 模拟实现源代码
template<class T>
struct list_node//节点
{list_node<T>* _next;list_node<T>* _prev;T _data;list_node(const T& x = T()):_next(nullptr), _prev(nullptr), _data(x){}
};
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* n):_node(n){}Ref operator*()//解引用{return _node->_data;}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& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}
};
template<class T>
class list//链表
{typedef list_node<T> node;
public:typedef __list_iterator<T, T&, T*> iterator;//普通迭代器typedef __list_iterator<T, const T&, const T*> const_iterator;//const迭代器iterator begin(){return iterator(_head->_next);//匿名对象的返回}const_iterator begin() const{return const_iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator end() const{return const_iterator(_head);}void empty_init()//空初始化{_head = new node;_head->_next = _head;_head->_prev = _head;}list(){empty_init();}//迭代器区间构造template <class Iterator>list(Iterator first, Iterator last){empty_init();while (first != last){push_back(*first);//push_back使用的前提是要有哨兵位的头结点++first;}}// 交换函数void swap(list<T>& tmp){std::swap(_head, tmp._head);}//现代拷贝构造list(const list<T>& lt){list<T> tmp(lt.begin(), lt.end());swap(tmp);}//现代赋值写法list<T>& operator=(list<T> lt){swap(lt);return *this;}~list()//要全部清理掉{clear();delete _head;_head = nullptr;}void clear()//不释放头结点{iterator it = begin();while (it != end()){it = erase(it);//这样也可以//erase(it++);}}void insert(iterator pos, const T& x){node* cur = pos._node;node* prev = cur->_prev;node* new_node = new node(x);prev->_next = new_node;new_node->_prev = prev;new_node->_next = cur;cur->_prev = new_node;}iterator erase(iterator pos){assert(pos != end());node* prev = pos._node->_prev;node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;return iterator(next);}//尾插void push_back(const T& x) const{//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){//node* head = _head->_next;//node* new_node = new node(x);//_head->_next = new_node;//new_node->_prev = _head;//new_node->_next = head;//head->_prev = new_node;insert(begin(), x);}//尾删void pop_back(){erase(--end());}//头删void pop_front(){erase(begin());}
private:node* _head;
};
四、list 的迭代器失效
当我们使用 erase 进行删除后,此时指向删除位置的迭代器就失效了,再次使用就会令程序崩溃。
因此若要多次删除,则需要在使用后利用 erase 的返回值更新迭代器,这样使用才不会出现错误。
int main()
{vector<int> v = { 1, 2,3,5,4,6 };list<int> lt(v.begin(), v.end());list<int>::iterator pos = find(lt.begin(), lt.end(), 3);for (int i = 0; i < 3; i++){pos = lt.erase(pos); //利用erase的返回值更新迭代器}for (auto e : lt) cout << e << " ";cout << endl;return 0;
}
五、list 和 vector的对比
| vector | list | |
| 底 层 结 构 | 动态顺序表,一段连续空间 | 带头结点的双向循环链表 |
| 随 机 访 问 | 支持随机访问,访问某个元素效率O(1) | 不支持随机访问,访问某个元素 效率O(N) |
| 插 入 和 删 除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂 度为O(N),插入时有可能需要增容,增容:开辟新空 间,拷贝元素,释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不 需要搬移元素,时间复杂度为 O(1) |
| 空 间 利 用 率 | 底层为连续空间,不容易造成内存碎片,空间利用率 高,缓存利用率高 | 底层节点动态开辟,小节点容易 造成内存碎片,空间利用率低, 缓存利用率低 |
| 迭 代 器 | 原生态指针 | 对原生态指针(节点指针)进行封装 |
| 迭 代 器 失 效 | 在插入元素时,要给所有的迭代器重新赋值,因为插入 元素有可能会导致重新扩容,致使原来迭代器失效,删 除时,当前迭代器需要重新赋值否则会失效 | 插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代 器失效,其他迭代器不受影响 |
| 使 用 场 景 | 需要高效存储,支持随机访问,不关心插入删除效率 | 大量插入和删除操作,不关心随机访问 |
本文要是有不足的地方,欢迎大家在下面评论,我会在第一时间更正。

相关文章:
【C++】STL之list深度剖析及模拟实现
目录 前言 一、list 的使用 1、构造函数 2、迭代器 3、增删查改 4、其他函数使用 二、list 的模拟实现 1、节点的创建 2、push_back 和 push_front 3、普通迭代器 4、const 迭代器 5、增删查改(insert、erase、pop_back、pop_front) 6、构造函数和析构函数 6.1、默认构造…...
解释器风格架构C# 代码
/*解释器风格架构是一种基于组件的设计架构,它将应用程序分解为一系列组件,每个组件负责处理特定的任务。这种架构有助于提高代码的可维护性和可扩展性。以下是如何使用C#实现解释器风格架构的步骤:定义组件:首先,定义…...
第七天:gec6818开发板QT和Ubuntu中QT安装连接sqlite3数据库驱动环境保姆教程
sqlite3数据库简介 帮助文档 SQL Programming 大多数关系型数的操作步骤:1)连接数据库 多数关系型数据库都是C/S模型 (Client/Server)sqlite3是一个本地的单文件关系型数据库,同样也有“连接”的过程 2)操作数据库 作为程序员&am…...
自制网页。
文章目录 注:代码中图片等素材均来自网络,侵删 20230920_213831 index.html <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-…...
MySQL单表查询和多表查询
一、单表查询 素材: 表名:worker-- 表中字段均为中文,比如 部门号 工资 职工号 参加工作等 CREATE TABLE worker (部门号 int(11) NOT NULL,职工号 int(11) NOT NULL,工作时间 date NOT NULL,工资 float(8,2) NOT NULL,政治面貌 varchar(10)…...
蓝桥等考Python组别四级006
第一部分:选择题 1、Python L4 (15分) 在Python中,符号“\n”代表( )。 换行空格退格注释正确答案:A 2、Python L4 (15分) 已知大写字母A的ASCII码值为…...
第3章-指标体系与数据可视化-3.2-描述性统计分析与绘图
目录 3.2.1 描述性统计进行数据探索 1. 变量度量类型与分布类型 度量类型 分布类型...
更直观地学习 Git 命令
前言 本文参考于 Learn Git Branching 这个有趣的 Git 学习网站。 在该网站,可以使用 show command 命令展示所有可用命令。 直接访问网站的sandbox。 本地篇 基础篇 git commit git commit将暂存区的修改提交到本地版本库并创建一个新的提交,新提…...
在 Vue 项目中添加字典翻译工具(二)
封装字段翻译组件,可以格式化字典、枚举、字段 优点: 使用简单,一次配置多次使用,缓存降低后端请求次数,扩展性强 没有缓存时造成单页面多次请求解决方法:axios添加缓存请求,防止多次请求&#…...
RDMA Shared Receive Queue(四)
参考知乎文章《RDMA之Shared Receive Queue》:https://zhuanlan.zhihu.com/p/279904125 SRQ SRQ全称为Shared Receive Queue,即共享接受队列。在QP中,SQ用于下发SEND/WRITE/READ等操作,而RQ只用于下发RECV操作,对于本…...
this关键字
作用:出现在成员方法,构造器中代表当前对象的地址,用于访问当前对象的成员变量、成员方法。this出现在 有参数构造器 中的用法 (this.成员变量 局部变量)this出现在 成员方法 中的用法 (this…...
缓存雪崩、缓存击穿、缓存穿透
缓存雪崩 当缓存中大量的键值对同时过期或者Redis宕机了,大量的请求就会直接打到数据库,这种现象就是缓存雪崩 应对策略 有四种,分别是“均匀设置过期时间”、“互斥锁”、“双key策略”、“设置逻辑过期时间,异步更新缓存” …...
Bigemap如何查看历史影像
工具 Bigemap gis office地图软件 BIGEMAP GIS Office-全能版 Bigemap APP_卫星地图APP_高清卫星地图APP 很多人都在寻找历史影像图,这块的需求是非常大,历史影像一般可以用于历史地貌的变迁分析,还原以前的生态场景,对范围面积…...
如何离线安装和使用pymysql操作mysql数据库
一、应用背景 在企业内部网络要使用python操作mysql数据库。然而,python未自带访问MySQL数据库的函数库pymysql,需要另外安装。网上有很多安装pymysql都需要互联网支持。本文主要阐述如何离线安装pymysql,并简要介绍pymysql如何进行mysql操作。 pymysq…...
Prometheus-监控Mysql进阶用法(1)(安装配置)
阿丹: 在开发和生产环境中有可能会出现慢mysql等问题,那么这里就需要我们优秀的程序员来进行监控和解决,那么如何借助云原生的监控系统来完成这个操作呢? 环境描述: 使用一台空白的阿里云服务器2核4G。 服务器基本安装…...
网络安全(黑客技术)自学内容
前言 一、什么是网络安全 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域,都有攻与防…...
linux centos7 安装mongodb7.0.1 及 mongosh2.0.1
下载数据库并解压 wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-7.0.1.tgz tar -zxf mongodb-linux-x86_64-rhel70-7.0.1.tgz #移动到/usr/local/mongo目录 mv mongodb-linux-x86_64-rhel70-7.0.1 /usr/local/mongodbmongosh 命令行下载 #下载命令行…...
c++ | makefile | 编译 | 链接库
简单记一下 看着人家总结的挺好的 点这...
n个骰子掷出m点的概率,C++实现
一.在骰子游戏中,我们会有猜点数的问题。我们该如何用算法来描述呢? 加入我们当前只有一个骰子,我们该如何算概率呢? 我们现在开始推导一下吧: 首先是一个骰子的情况,我们可以简单的知道n个骰子一共可以掷出…...
【JUC系列-08】深入理解CyclicBarrier底层原理和基本使用
JUC系列整体栏目 内容链接地址【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786【三】熟练掌握Atomic原子系列基本…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
