C++初阶(十六)--STL--list的模拟实现
目录
结点类的实现
迭代器类的模拟实现
迭代器类的模板参数说明
构造函数
*运算符重载
->运算符的重载
++运算符的重载
--运算符重载
!=运算符重载
==运算符重载
list类的模拟实现
成员变量
默认成员函数
构造函数
拷贝构造函数
赋值运算符重载
迭代器相关函数
begin和end
插入and删除
insert
erase
头插和头删
尾插和尾删
其他函数
size
resize
empty
clear
本篇博客会使用较多的模板编程,模拟实现迭代器,加深对模板编程的理解,这里只实现常用接口的模拟,部分不是很常用的接口用到时在翻阅手册即可,大多接口的使用在上篇博客中均已提到。所以本篇博客的重点在迭代器的模拟,因为链表的实现已经在C语言中的数据结构讲解过了。
本次我们需要实现三个类,我们经常说list在底层实现时就是一个链表,更准确来说,list实际上是一个带头双向循环链表。
因此我们要实现一个list肯定要实现一个结点类,这个结点里面存放着的信息有数据,前一个结点的地址和后一个结点的地址。对于该结点,我们只需要写一个构造函数,它的析构由我们的list的析构函数完成。
结点类的实现
template <class T>
struct list_node
{T _date;list_node* _prev;list_node* _next;list_node(const T& x = T()):_date(x), _prev(nullptr), _next(nullptr){}
};
这里解释一下构造函数的参数:
- 接受各种类型的参数
- 构造函数
list_node(const T& x = T())
中的参数x
是一个const T&
类型,即一个常量引用。使用引用作为参数可以避免在传递对象时进行不必要的拷贝。当T
是一个大型对象时,如包含许多成员变量的结构体或类,传引用可以大大提高效率。例如,如果T
是一个Matrix
类(用于表示矩阵),它可能有很多数据成员来存储矩阵的元素。如果构造函数不使用引用,每次创建list_node
节点来存储Matrix
对象时,都需要复制整个Matrix
对象,这会消耗大量的时间和空间。- 同时,参数被声明为
const
,这保证了在构造函数内部不能修改传入的参数x
的值。这对于那些不应该在构造函数中被修改的对象是非常重要的。例如,当T
是一个不可变的对象(如std::string_view
),const
保证了其内容不会被改变。- 提供默认构造的灵活性
x = T()
这部分设置了默认值。当用户没有显式传入参数时,构造函数会使用T
类型的默认构造值来初始化_date
成员变量。这使得list_node
的构造更加灵活。例如,当T
是一个基本数据类型(如int
),默认构造值就是 0;当T
是一个类,只要这个类有默认构造函数,就可以自动调用它来初始化_date
。这样,既可以使用list_node<int> node1;
(此时_date
被初始化为 0)这种方式创建节点,也可以使用list_node<int> node2(5);
(此时_date
被初始化为 5)这种方式,根据具体需求来初始化节点。
迭代器类的模拟实现
除了这两个类,我们还要单独实现一个迭代器的类。
之前模拟实现string和vector时都没有说要实现一个迭代器类,为什么实现list的时候就需要实现一个迭代器类了呢?
因为string和vector对象都将其数据存储在了一块连续的内存空间,我们通过指针进行自增、自减以及解引用等操作,就可以对相应位置的数据进行一系列操作,因此string和vector当中的迭代器就是原生指针。
但是对于list来说,其各个结点在内存当中的位置是随机的,并不是连续的,我们无法仅通过结点指针的自增、自减以及解引用等操作对相应结点的数据进行操作。
既然list的结点指针的行为不满足迭代器定义,那么我们可以对这个结点指针进行封装,对结点指针的各种运算符操作进行重载,使得我们可以用和string和vector当中的迭代器一样的方式使用list当中的迭代器。例如,当你使用list当中的迭代器进行自增操作时,实际上执行了p = p->next语句,只是你不知道而已。
总结: list迭代器类,实际上就是对结点指针进行了封装,对其各种运算符进行了重载,使得结点指针的各种行为看起来和普通指针一样。(例如,对结点指针自增就能指向下一个结点)
迭代器类的模板参数说明
template<class T, class Ref, class Ptr>
这里使用了三个模板参数。T
通常与链表节点存储的数据类型相关,用于确保迭代器操作的数据类型一致性。Ref
和 Ptr
则分别用于定义解引用操作符 *
和箭头操作符 ->
返回的引用类型和指针类型,这样可以根据需要灵活地返回不同类型的引用或指针,比如对于常量对象的迭代可以返回常量引用等。
- 例如,当我们想要遍历一个常量链表时,希望解引用迭代器得到的是常量引用(这样就不会意外修改链表中的数据),此时就可以通过调整
Ref
和Ptr
的类型来实现。如果只是用一个模板参数,就很难灵活地满足这种不同访问需求的场景。
这样说可能也不是太理解,但我们后续会list类中typedef两个迭代器类型,普通迭代器和const迭代器。
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;
这里我们就可以看出,迭代器类的模板参数列表当中的Ref和Ptr分别代表的是引用类型和指针类型。
当我们使用普通迭代器时,编译器就会实例化出一个普通迭代器对象;当我们使用const迭代器时,编译器就会实例化出一个const迭代器对象。
若该迭代器类不设计三个模板参数,那么就不能很好的区分普通迭代器和const迭代器。
构造函数
list_iterator(Node* node):_node(node)
{}
*运算符重载
当我们使用解引用操作符时,是想得到该位置的数据内容。因此,我们直接返回当前结点指针所指结点的数据即可,但是这里需要使用引用返回,因为解引用后可能需要对数据进行修改。
Ref operator*()//T&
{return _node->_date;//返回结点中的数据
}
->运算符的重载
某些情景下,我们使用迭代器的时候可能会用到->运算符。
想想如下场景:
当list容器当中的每个结点存储的不是内置类型,而是自定义类型,例如日期类,那么当我们拿到一个位置的迭代器时,我们可能会使用->运算符访问Date的成员:
list<Date> lt;Date d1(2021, 8, 10);Date d2(1980, 4, 3);Date d3(1931, 6, 29);lt.push_back(d1);lt.push_back(d2);lt.push_back(d3);list<Date>::iterator pos = lt.begin();cout << pos->_year << endl; //输出第一个日期的年份
注意: 使用pos->_year这种访问方式时,需要将日期类的成员变量设置为公有。
对于->运算符的重载,我们直接返回结点当中所存储数据的地址即可。
Ptr operator->()
{return &_node->_date;//返回结点指针所指向的地址
}
实际上这里本来是应该有两个->的,第一个箭头是pos ->去调用重载的operator->返回Date* 的指针,第二个箭头是Date* 的指针去访问对象当中的成员变量_year。
但是一个地方出现两个箭头,程序的可读性太差了,所以编译器做了特殊识别处理,为了增加程序的可读性,省略了一个箭头。
++运算符的重载
首先是前置++,前置++原本的作用是将数据自增,然后返回自增后的数据。我们的目的是让结点指针的行为看起来更像普通指针,那么对于结点指针的前置++,我们就应该先让结点指针指向后一个结点,然后再返回“自增”后的结点指针即可。这里返回引用而不是返回值,可以避免不必要的对象拷贝
Self& operator++()
{return _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;
}
list类的模拟实现
成员变量
这个头节点也叫做哨兵位结点,是不可访问的,然后我们给一个_size记录list的长度。
private:Node* _head = nullptr;size_t _size;
默认成员函数
构造函数
list是一个带头双向循环链表,在构造一个list对象时,直接申请一个头结点,并让其前驱指针和后继指针都指向自己即可。
void empty_Init()
{_head = new Node();_head->_next = _head;_head->_prev = _head;_size = 0;
}list(){empty_Init();}
为了后序使用方便,我们将构造头结点封装成了一个函数
拷贝构造函数
拷贝构造函数就是根据所给list容器,拷贝构造出一个对象。对于拷贝构造函数,我们先申请一个头结点,并让其前驱指针和后继指针都指向自己,然后将所给容器当中的数据,通过遍历的方式一个个尾插到新构造的容器后面即可。
list(const list& lt)
{empty_Init();for (auto e : lt){push_back(e);}
}
赋值运算符重载
这里依然是这种简单的写法,使用swap。
list<T>& operator = (List<T> lt)
{swap(lt);return *this;
}
也可传引用,清空之后,然后一个个尾插到原容器里面
list<T>& operator = (const list<T>& lt){if (< != this){clear();for (auto e : lt){push_back(e);}}return *this;}
析构函数
对对象进行析构时,首先调用clear函数清理容器当中的数据,然后将头结点释放,最后将头指针置空即可。
~list()
{clear();delete _head;_head = nullptr;
}
迭代器相关函数
begin和end
首先我们应该明确的是:begin函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器。
对于list这个带头双向循环链表来说,其第一个有效数据的迭代器就是使用头结点后一个结点的地址构造出来的迭代器,而其最后一个有效数据的下一个位置的迭代器就是使用头结点的地址构造出来的迭代器。(最后一个结点的下一个结点就是头结点)
iterator begin()
{return iterator(_head->_next);
}iterator end()
{return iterator(_head);
}const_iterator begin() const
{return const_iterator(_head->_next);
}const_iterator end() const
{return const_iterator(_head);
}
插入and删除
insert
插入的逻辑并不难,我们这里跟STL接口容器风格统一,所以我们也返回一个迭代器。这里不会出现迭代器失效问题的。
iterator insert(iterator pos, const T& val){Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;prev->_next = newnode;newnode->_next = cur;newnode->_prev = prev;cur->_prev = newnode;++_size;return iterator(newnode);}
erase
删除的逻辑也不难,注意不要删空了之后还要继续删除。
iterator erase(iterator pos){assert(pos != end());Node* del = pos._node;Node* prev = del->_prev;Node* next = del->_next;prev->_next = next;next->_prev = prev;delete del;--_size;return iterator(next);}
这里我们要返回迭代器是方便一些后续的操作,后面在clear和resize中可以看到,方便我们操作下一个结点,而不需要重新获取迭代器,进行复杂的指针操作。而且在遍历链表时,可能需要删除某些满足特定条件的节点。如果
erase
函数不返回迭代器,在删除一个节点后,迭代器就会失效,导致后续的遍历无法继续正常进行。也为了与STL风格统一。
头插和头删
这里都可以直接复用前面的接口
void push_front(const T& x){insert(begin(), x);}void pop_front(){erase(begin());}
尾插和尾删
void push_back()
{insert(end(), x);
}
void pop_back()
{erase(--end());
}
其他函数
size
我们的成员变量里面是有_size的,并且我们每次的插入和删除都会自增和自减_size,所以这里直接返回_size即可
size_t size()
{return _size;
}
resize
resize函数的规则:
1.若当前容器的size小于所给n,则尾插结点,直到size等于n为止。
2.若当前容器的size大于所给n,则只保留前n个有效数据。
实现resize函数时,不要直接调用size函数获取当前容器的有效数据个数,因为当你调用size函数后就已经遍历了一次容器了,而如果结果是size大于n,那么还需要遍历容器,找到第n个有效结点并释放之后的结点。
这里实现resize的方法是,设置一个变量len,用于记录当前所遍历的数据个数,然后开始遍历容器,在遍历过程中:
当len大于或是等于n时遍历结束,此时说明该结点后的结点都应该被释放,将之后的结点释放即可。
当容器遍历完毕时遍历结束,此时说明容器当中的有效数据个数小于n,则需要尾插结点,直到容器当中的有效数据个数为n时停止尾插即可。
void resize(size_t n,const T& x= T())
{//int len = _size();int len = 0;iterator it = begin();while (it != end() && len < n){len++;it++;}if (len <n){while (len != n){push_back(x);len++;}} else//len>n{while (it != end()){it = erase(it);}}_size = n;
}
小贴士:const T& x = T()和const T& x做参数有什么区别?
基本理解
- 可以这样理解,
const T& x = T()
是带有缺省值的参数声明,而const T& x
是没有缺省值的参数声明。函数调用灵活性差异
- 带缺省值的参数:
- 当参数是
const T& x = T()
形式时,在调用函数时具有更大的灵活性。因为有了缺省值,调用者可以选择不传递这个参数,此时函数会使用默认构造的T
类型的值作为参数x
的值。这在一些情况下可以简化函数调用,尤其是当参数的默认值在很多情况下都能满足函数的基本操作需求时。- 例如,有一个函数
void printValue(const int& x = int())
,可以像printValue()
这样调用,函数内部会将x
当作0
(假设int
的默认构造是0
)来处理,也可以像printValue(5)
这样传入一个具体的值来覆盖默认值。- 不带缺省值的参数:
- 对于
const T& x
这种没有缺省值的参数声明,函数调用时必须提供一个符合T
类型的参数。这使得函数在调用时更加严格,要求调用者明确地提供参数值。这种方式适用于那些参数对于函数操作是必不可少的,不能使用默认值替代的情况。- 例如,
void processString(const std::string& x)
,调用这个函数时必须提供一个std::string
对象,如processString("example")
,不能省略参数。
empty
bool empty() const
{return begin == end;
}
clear
void clear()
{auto it = begin();while (it != end()){it = erase(it);}
}
swap
swap函数用于交换两个容器,list容器当中存储的实际上就只有链表的头指针,我们将这两个容器当中的头指针交换即可。
void swap(list<T>& lt){std::swap(_head,lt);std::swap(_size, lt.size());}
代码:
这是.h文件中的代码,复制到.h文件中即可。然后自己创建一个.cpp文件引入.h文件,写代码调用接口进行调试即可。
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <assert.h>
#include <iostream>
using namespace std;namespace My_List
{template <class T>struct list_node{T _date;list_node* _prev;list_node* _next;list_node(const T& x = T()):_date(x), _prev(nullptr), _next(nullptr){}};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* node):_node(node){}Ref operator*()//T&{return _node->_date;//返回结点中的数据}Ptr operator->(){return &_node->_date;//返回结点指针所指向的地址}//前置++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{public:typedef list_node<T> Node;typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;void empty_Init(){_head = new Node();_head->_next = _head;_head->_prev = _head;_size = 0;}list(){empty_Init();}list(const list& lt){empty_Init();for (auto e : lt){push_back(e);}}~list(){clear();delete _head;_head = nullptr;}/*list<T>& operator = (const list<T>& lt){if (< != this){clear();for (auto e : lt){push_back(e);}}return *this;}*/list<T>& operator = (list<T> lt){swap(lt);return *this;}//迭代器iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}//void push_back(const T& x)//{// //尾插实际上也是一种头插// Node* newnode = new Node(x);// Node* tail = _head->_prev;//记录尾部节点// tail->_next = newnode;// newnode->_prev = tail;// newnode->_next = _head;// _head->_prev = newnode;//}iterator insert(iterator pos, const T& val){Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;prev->_next = newnode;newnode->_next = cur;newnode->_prev = prev;cur->_prev = newnode;++_size;return iterator(newnode);}iterator erase(iterator pos){assert(pos != end());Node* del = pos._node;Node* prev = del->_prev;Node* next = del->_next;prev->_next = next;next->_prev = prev;delete del;--_size;return iterator(next);}void push_front(const T& x){insert(begin(), x);}void pop_front(){erase(begin());}void push_back(const T& x){insert(end(), x);}void pop_back(){erase(--end());}void clear(){auto it = begin();while (it != end()){it = erase(it);}}size_t size(){return _size;}void resize(size_t n,const T& x= T()){//int len = _size();int len = 0;iterator it = begin();len = 0;while (it != end() && len < n){len++;it++;}if (len <n){while (len != n){push_back(x);len++;}} else//len>n{while (it != end()){it = erase(it);}}_size = n;}bool empty() const{return begin == end;}void swap(list<T>& lt){std::swap(_head,lt._head);std::swap(_size, lt._size);}private:Node* _head = nullptr;size_t _size;};}
本篇博客到此结束,如有问题,欢迎各位评论区留言~
相关文章:
C++初阶(十六)--STL--list的模拟实现
目录 结点类的实现 迭代器类的模拟实现 迭代器类的模板参数说明 构造函数 *运算符重载 ->运算符的重载 运算符的重载 --运算符重载 !运算符重载 运算符重载 list类的模拟实现 成员变量 默认成员函数 构造函数 拷贝构造函数 赋值运算符重载 迭代器相关函数 …...
树莓集团:以人工智能为核心,打造数字化生态运营新典范
在当今数字化浪潮席卷全球的背景下,各行各业都在积极探索数字化转型的路径。作为数字产业的领军者,树莓集团凭借其深厚的技术积累和创新理念,在人工智能、大数据、云计算等前沿技术领域不断突破,成功打造了一个以人工智能为核心的…...
基于深度学习的卷积神经网络十二生肖图像识别系统(PyQt5界面+数据集+训练代码)
本研究提出了一种基于深度学习的十二生肖图像识别系统,旨在利用卷积神经网络(CNN)进行图像分类,特别是十二生肖图像的自动识别。系统的核心采用了两种经典的深度学习模型:ResNet50和VGG16,进行图像的特征提…...
Torchtune在AMD GPU上的使用指南:利用多GPU能力进行LLM微调与扩展
Torchtune on AMD GPUs How-To Guide: Fine-tuning and Scaling LLMs with Multi-GPU Power — ROCm Blogs 这篇博客提供了一份详细的使用Torchtune在AMD GPU上微调和扩展大型语言模型(LLM)的指南。Torchtune 是一个PyTorch库,旨在让您轻松地…...
ESLint
代码规范 一套写代码的约定规则;比如赋值符号左右是否需要空格,一句话结束是否要加;.... 代码规范错误 如果你的代码不符合standard的要求,ESLint(脚手架里配的东西)会告诉你哪个文件第几行错了 解决代码…...
小程序-基于java+SpringBoot+Vue的微信小程序养老院系统设计与实现
项目运行 1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境:IDEA,Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境:Tomcat 7.x,8.x,9.x版本均可 4.硬件环境:…...
如何具体实现商品详情的提取?
在电商领域,获取商品详情信息对于市场分析、价格比较、商品推荐等应用场景至关重要。本文将详细介绍如何使用Java编写爬虫程序,以合法合规的方式获取淘宝商品的详情信息,并提供详细的代码示例。 1. 环境准备 在开始编写爬虫之前,…...
antd table 自定义表头过滤表格内容
注意:该功能只能过滤可一次性返回全部数据的表格,通过接口分页查询的请自主按照需求改动哈~ 实现步骤: 1.在要过滤的列表表头增加过滤图标,点击图标显示浮窗 2.浮窗内显示整列可选选项,通过勾选单选或者全选、搜索框来…...
高效处理 iOS 应用中的大规模礼物数据:以直播项目为例(1-礼物池)
引言 在现代iOS应用开发中,处理大规模数据是一个常见的挑战。尤其实在直播项目中,礼物面板作为展示用户互动的重要部分,通常需要实时显示海量的礼物数据。这些数据不仅涉及到不同的区域、主播的动态差异,还需要保证高效的加载与渲…...
Maven - 优雅的管理多模块应用的统一版本号
文章目录 概述一、使用 versions-maven-plugin 插件1. 在主 pom.xml 中定义插件2. 修改版本号3. 回退修改4. 提交修改 二、使用占位符统一管理版本号1. 在主 pom.xml 中定义占位符2. 使用 flatten-maven-plugin 插件自动替换占位符3. 修改版本号4. 为什么这种方式更方便&#x…...
C++设计模式(观察者模式)
一、介绍 1.动机 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”,即一个对象的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵…...
【unity】WebSocket 与 EventSource 的区别
WebSocket 也是一种很好的选择,尤其是在需要进行 双向实时通信(例如聊天应用、实时数据流等)时。与 EventSource 不同,WebSocket 允许客户端和服务器之间建立一个持久的、全双工的通信通道。两者的区别和适用场景如下:…...
从ETL到DataOps:WhaleStudio替代Informatica,实现信创化升级
作者 | 白鲸开源 姜维 在数据集成和调度的领域,Informatica曾经是公认的权威工具。其强大的ETL功能、多年积累的市场经验,使其成为众多企业数据处理的核心工具。 然而,随着新一代大数据平台的迅速崛起,以及信创化改造的要求愈发严…...
第四十二篇 EfficientNet:重新思考卷积神经网络的模型缩放
文章目录 摘要1、简介2、相关工作3、复合模型缩放3.1、 问题公式化3.2、扩展维度3.3、复合比例 4、EfficientNet架构5、实验5.1、扩展MobileNets和ResNets5.2、EfficientNet的ImageNet结果5.3、EfficientNet的迁移学习结果 6、讨论7、结论 摘要 卷积神经网络(ConvNets)通常在固…...
[OpenHarmony5.0][Docker][环境]OpenHarmony5.0 Docker编译环境镜像下载以及使用方式
T. 已测试目录 主机类型主机版本Docker镜像版本结果WSL2Ubuntu22.04Ubuntu20.04PASSWSL2Ubuntu22.04Ubuntu18.04PASS R. 软硬件要求: 编译硬件需求:做多系统测试,磁盘500GB起步(固态)(机械会卡死),内存3…...
Web Worker 和 WebSocket的区别
Web Worker(消息传递机制) 定义:是为了在浏览器中提供多线程支持,允许 JavaScript 在后台线程运行,而不阻塞主线程。它非常适合执行耗时的计算任务或处理大量数据,避免主线程(通常是 UI 线程&a…...
vmware Ubuntu2004运行STAR-Searcher
github链接 安装ros noetic gazebo11 略 gazebo更新方法 sudo sh -c echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable lsb_release -cs main" > /etc/apt/sources.list.d/gazebo-stable.list wget https://packages.osrfoundation.org/gaz…...
04_JavaScript引入到文件
JavaScript引入到文件 嵌入到HTML文件中 <body> <script> var age 20 </script></body> 引入本地独立JS文件 <body> <script type"text/javascript" src"./itbaizhan.js"> </script></body> 引入网络来…...
计算机网络的功能
目录 信息交换 资源共享 分布式处理 可靠性增强 集中管理 信息交换 计算机网络最基本的功能之一是允许不同设备之间的数据通信。这包括电子邮件的发送和接收、即时消息的传递、文件传输等。通过网络,用户可以轻松地与全球各地的其他人进行沟通和协作。 信息交…...
38 基于单片机的宠物喂食(ESP8266、红外、电机)
目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52单片机,采用L298N驱动连接P2.3和P2.4口进行电机驱动, 然后串口连接P3.0和P3.1模拟ESP8266, 红外传感器连接ADC0832数模转换器连接单片机的P1.0~P1.…...
Git仓库迁移到远程仓库(源码、分支、提交)
单个迁移仓库 一、迁移仓库 1.准备工作 > 手动在电脑创建一个临时文件夹,CMD进入该目录 > 远程仓库上创建一个同名的空仓库 2.CMD命令:拉取旧Git仓库(包含提交、分支、源码) $ git clone --bare http://git.domain.cn/…...
Go语言压缩文件处理
目录 Go 语言压缩文件处理1. 压缩文件:Zip函数2. 解压文件:UnZip 函数3. 小结 Go 语言压缩文件处理 在现代的应用开发中,处理压缩文件(如 .zip 格式)是常见的需求。Go 语言提供了内置的 archive/zip 包来处理 .zip 文…...
Libevent库-http通信不同请求方式的处理
做项目的时候用到了http通信,同事用libevent库写的,特此记录后端从前端拿到消息后的处理方式 void CHTTPTest::request(const std::any & data) {// data 是从前端拿到的数据void *obj std::any_cast<void *>(data); // std::any是C17新标准…...
Vue3 v-if与v-show的区别
v-if 与 v-show 我们都可以在开发中用于条件渲染,在面试时也是个常考的题目 实现原理 v-if:是真正的条件渲染,当v-if true时,元素会被创建、渲染,并插入到dom树中,这个过程会耗费系统的资源,当…...
同时在github和gitee配置密钥
同时在github和gitee配置密钥 1. 生成不同的 SSH 密钥 为每个平台生成单独的 SSH 密钥。 # 为 GitHub 生成密钥(默认文件路径为 ~/.ssh/github_id_rsa) ssh-keygen -t rsa -b 4096 -C "your_github_emailexample.com" -f ~/.ssh/github_id_…...
Scala—数组(不可变数组Array、可变数组ArrayBuffer)用法详解
Scala集合概述-链接 大家可以点击上方链接,先对Scala的集合有一个整体的概念🤣🤣🤣 在 Scala 中,数组是一种特殊的集合类型,可以是可变的也可以是不可变的。 1. 不可变数组 在 Scala 中,不可变…...
Failed to find SV in PRN block of SINEX file (Name svnav.dat)
gamit 精密星历生成失败 gamit svnav.dat没更新 下载svnav.dat.allgnss 重命名成 svnav.dat ,替换到tables,即可。...
H.265流媒体播放器EasyPlayer.js无插件H5播放器关于移动端(H5)切换网络的时候,播放器会触发什么事件
EasyPlayer.js无插件H5播放器作为一款功能全面的H5流媒体播放器,凭借其多种协议支持、多种解码方式、丰富的渲染元素和强大的应用功能,以及出色的跨平台兼容性,为用户提供了高度定制化的选项和优化的播放体验。无论是视频直播还是点播&#x…...
Ubuntu上使用自带python创建虚拟环境
虚拟环境让项目之间的依赖关系更加清晰,也可以避免全局安装的包的版本冲突问题。 1.查看python Ubuntu上一般都是自带python的,查看python版本及指向(使用的话python要换成python3): ls -l /usr/bin | grep python 2.安装python虚拟环境工…...
对智能电视直播App的恶意监控
首先我们要指出中国广电总局推出的一个政策性文件是恶意监控的始作俑者,这个广电总局的政策性文件禁止智能电视和电视盒子安装直播软件。应该说这个政策性文件是为了保护特殊利益集团,阻挠技术进步和发展的。 有那么一些电视机和电视盒子的厂商和电信运…...
网络原理(一):应用层自定义协议的信息组织格式 HTTP 前置知识
目录 1. 应用层 2. 自定义协议 2.1 根据需求 > 明确传输信息 2.2 约定好信息组织的格式 2.2.1 行文本 2.2.2 xml 2.2.3 json 2.2.4 protobuf 3. HTTP 协议 3.1 特点 4. 抓包工具 1. 应用层 在前面的博客中, 我们了解了 TCP/IP 五层协议模型: 应用层传输层网络层…...
微软企业邮箱:安全可靠的企业级邮件服务!
微软企业邮箱的设置步骤?如何注册使用烽火域名邮箱? 微软企业邮箱作为一款专为企业设计的邮件服务,不仅提供了高效便捷的通信工具,更在安全性、可靠性和功能性方面树立了行业标杆。烽火将深入探讨微软企业邮箱的多重优势。 微软…...
matlab导出3D彩色模型(surface类转stl,并对白模上色)
在matlab中绘制3维图形时,需要将3维图形导出到PPT中展示。但是直接导出图片效果欠佳,无法全方位展示。 最近学习了如何将matlab中的图形导出为stl模型,然后再采用简单的方法对模型上色。 中间尝试过matlab导出stl、ply、3dm等多种格式&…...
【JavaScript】async /await异步详解2 promise举例
在 JavaScript 中,async 和 await 是处理异步操作的强大工具。结合 try...catch 语句,你可以优雅地处理异步操作中的错误。 基本用法 定义异步函数:使用 async 关键字。等待 Promise 解析:使用 await 关键字。错误处理ÿ…...
Android 设备使用 Wireshark 工具进行网络抓包
背景 电脑和手机连接同一网络,想使用wireshark抓包工具抓取Android手机网络日志,有以下两种连接方法: Wi-Fi 网络抓包。USB 网络共享抓包。需要USB 数据线将手机连接到电脑,并在开发者模式中启用 USB 网络共享。 查看设备连接信…...
封装类与封装函数
目录结构 src/ ├── utils/ │ ├── test.js │ ├── Calculator.js ├── views/ │ ├── Home.vue ├── App.vue 共同点:模块导出与模块引入 封装函数 场景 简单、轻量级和性能敏感的场景,适合快速开发和维护。 优 可维护性&…...
【java】接口幂等性的实现
文章目录 1. 引言介绍幂等性的概念为什么需要在Java接口中实现幂等性 2. 使用幂等表实现幂等性什么是幂等表如何设计幂等表示例:Java代码实现使用幂等表使用幂等表实现幂等性 3. 利用Nginx Lua 和 Redis实现幂等性Nginx和Lua的作用简介介绍Redis的SETNX命令架构设计…...
Leetcode 1.两数之和
题目意在对数组的快速查找 思路:哈希表 先创建一个哈希表,然后对数组进行遍历,iter代表用目标值依次减去遍历数组中的元素后得出的值,如果这个值在map中存在,则返回其索引和当前数组元素中的索引;若不存…...
GitLab指定用户分配合并权限
进入项目 -》 Project Settings Repository -》展开 Protected branches -》 添加要保护的分支,设置角色 管理用户角色权限 查看到不同用户的角色,一般设置Developer只有Merger Request权限,Maintainer还有Merge审批权限 GitLab 中的权限…...
实战优化公司线上系统JVM:从基础到高级
引言 Java虚拟机(JVM)是Java语言的核心组件,它使得Java程序能够实现“一次编写,到处运行”的跨平台特性。在现代应用程序中,JVM的性能和稳定性直接影响到系统的整体表现。本文将深入探讨JVM的基础知识、基本特点、定义…...
实时数据开发 | 一文理解Flink窗口机制
窗口操作在流处理和批处理之间起到了桥梁的作用。 Flink引擎本质上是流式引擎,认为批处理是流处理的一个特例。因此,通过窗口将流数据划分为有限大小的集合,使得在这些有界的数据集上可以进行批处理风格的计算。 通过配置窗口的参数…...
【云原生系列】迁移云上需要考虑哪些问题
云计算已经成为现代企业架构中不可或缺的一部分。越来越多的公司正在将他们的应用、数据和基础设施迁移到云平台上,以便更好地应对快速变化的市场需求、提高运营效率并降低成本。然而,迁移到云端并不是一件轻松的事情。涉及到多个技术、业务和管理层面的…...
1、Three.js开端准备环境
准备工作 从 CDN 导入 1.安装 VSCode 2.安装 Node.js 3.查看Three.js最新版本 4.如何cdn引入: https://cdn.jsdelivr.net/npm/threev版本号/build/three.module.js 例如:https://cdn.jsdelivr.net/npm/threev0.170.0/build/three.module.js 我们需要…...
go 和java 编写方式的理解
1. go 推荐写流水账式的代码(非贬义),自己管自己。java喜欢封装各种接口供外部调用,让别人来管自己。 2. 因为协程的存在, go的变量作用域聚集在方法内部,即函数不可重入,而java线程的限制&…...
华为ENSP--BGP路由协议实验详解
项目背景 随着A公司网络规模的增长和新业务对互联网接入速度及稳定性需求的提升,公司决定升级其网络设施。为此,A公司向运营商B租用了两条线路以接入网络,旨在提高网络资源的利用率,并增强网络的安全性、稳定性和可靠性,从而为用户提供更优质的服务和体验 在这一背景下,正…...
LLM*:路径规划的大型语言模型增强增量启发式搜索
路径规划是机器人技术和自主导航中的一个基本科学问题,需要从起点到目的地推导出有效的路线,同时避开障碍物。A* 及其变体等传统算法能够确保路径有效性,但随着状态空间的增长,计算和内存效率会严重降低。相反,大型语言…...
UE5 MakeSlateBrush 节点的作用
在 Unreal Engine 5 (UE5) 中,MakeSlateBrush 节点是用于 创建或设置一个 Slate Brush 的节点。Slate 是 Unreal Engine 中的 UI 框架,用于构建编辑器界面以及游戏中的用户界面(UI)。Slate Brush 是 Slate UI 中的一种资源类型&am…...
机载视频流回传+编解码方案
无线网络,低带宽场景。不能直接转发ROS raw image(10MB/s),而要压缩(编码)后再传输。可以用rtsp的udp传输或者直接传输话题,压缩方法有theora(ROS image_transport默认支持ÿ…...
小程序 - 婚礼邀请函
小程序页面和样式练习 - 婚礼邀请函小程序开发笔记 目录 婚礼邀请函 准备工作 加载静态资源 项目初始化 标签栏的配置 各页面导航栏标题配置 全局导航栏样式配置 公共样式的编写 项目内容 邀请函页面内容 邀请函页面样式 照片页面内容 照片墙页面样式 美好时光页…...
命令行使用ssh隧道连接远程mysql
本地电脑A 跳板机B 主机2.2.2.2 用户名 B ssh端口号22 登录密码bbb 远程mysql C 地址 3.3.3.3 端口号3306 用户名C 密码ccc A需要通过跳板机B才能访问C; navicat中配置ssh可以实现在A电脑上访问C 如何实现本地代码中访问C呢? # 假设本地使…...