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

STL标准库与泛型编程(侯捷)笔记3

STL标准库与泛型编程(侯捷)

本文是学习笔记,仅供个人学习使用。如有侵权,请联系删除。

参考链接

Youbute: 侯捷-STL标准库与泛型编程

B站: 侯捷 - STL

Github:STL源码剖析中源码 https://github.com/SilverMaple/STLSourceCodeNote/tree/master

Github:课程ppt和源码 https://github.com/ZachL1/Bilibili-plus

文章目录

  • STL标准库与泛型编程(侯捷)
    • 16 vector深度探索
    • 17 array、forward list深度探索
    • 18 deque、queue和 stack深度探索(上)
      • 容器deque
    • 19 deque、queue和 stack深度探索(下)
      • 容器queue
      • 容器stack
      • queue和stack,关于其iterator和底层结构
    • 后记

介绍vector, array, deque, queue, stack的底层实现

16 vector深度探索

Vector将元素复制到内部的dynamic array中。元素之间总是存在一定的顺序,所以vector是一种有序集合(ordered collection)。 Vector支持随机访问,因此只要知道位置,你可以在常量时间内访问任何一个元素。 Vector提供随机访问迭代器,所以适用于任何STL算法。

如果你在末端附加或删除元素,vector的效率相当好。但如果你在前端或中段安插或删除元素,效率就不怎么样了,因为作用点之后的每一个元素都必须移到另一位置,而每一次移动都得调用assignment操作符。

Vector是动态数组,它会进行扩充,但不是原地扩充,而是当数组空间用完时,它会在某个地方重新分配内存空间(原来的两倍大小),再把先前的内容搬过去。

vector底部有三个指针,start, finish, end_of_storage

在C++中,std::vector 是一个动态数组,它的底层通常包含三个指针成员:startfinishend_of_storage

  1. start: 这是指向数组的第一个元素的指针,表示 vector 的起始位置。

  2. finish: 这是指向数组最后一个元素的下一个位置的指针,表示 vector 当前存储的元素范围结束位置。注意,finish 指向的位置是当前存储元素的末尾的下一个位置,而不是最后一个元素的位置。

  3. end_of_storage: 这是指向 vector 内存空间的末尾的下一个位置的指针。当 vector 的元素数量接近其容量时,end_of_storage 可能指向当前分配内存的末尾。如果要添加更多的元素导致超过当前分配的内存空间,vector 可能需要重新分配更大的内存块,然后 end_of_storage 会被更新为新的内存块的末尾。

这些指针的作用是帮助 vector 进行动态内存管理。startfinish 之间的部分是当前 vector 存储的元素,而 finishend_of_storage 之间的部分是 vector 预分配但尚未使用的内存空间。当 vector 的元素数量增加时,它可能需要重新分配内存,然后更新 end_of_storage 指针,以确保有足够的空间来容纳更多的元素。

在这里插入图片描述

现在看看两倍空间增长是怎么做的

下面是push_back的实现,当前分配的空间用完的时候,会调用insert_aux函数

在这里插入图片描述

insert_aux关于空间两倍增长的部分如下图所示

const size_type len = old_size != 0 ? 2 * old_size : 1;

下面的try catch里面就是具体的从旧位置把元素拷贝到新位置的源代码,分别是:把原vector的内容拷贝到新vector,然后为新元素赋值,新的finish指针后移,如果有insert函数,还需要把insert后面的元素复制过来。

后面还会把原来旧的vector空间释放掉

在这里插入图片描述

vector’s iterator

list的迭代器设计为一个class,这里vector的迭代器就是一个指针T *, 然后借助前面介绍的iterator_traits的偏特化(范围的偏特化,从接收T变成接收指针T *),会把associated types里面的 value_type直接指定为T,而不是基于类的迭代器的I::value_type。下图是GNU C++2.9版本的内容

在这里插入图片描述

下面是对GNU C++4.9版本的vector的介绍:多了几个类,但是izeof(vector<T>)还是12B,因为vector继承自_Vector_base, 它里面有一个_Vector_impl的成员,它里面的data是三个指针,在32位电脑里面一个指针是4B,因此在32位电脑上vector的大小是12B。

在这里插入图片描述

GNU C++4.9版本的vector的iterator呢?

下图所示的设计很复杂,经过层层推导,可以看到_Iterator _M_current变量的类型也是_Tp*即上面GNU C++2.9版本的T*类型。

在这里插入图片描述

而且,GNU C++4.9版本的vector的iterator不再是G++2.9版本的指针T*,而是vector<T>::iterator,如下图箭头所指。

在这里插入图片描述

17 array、forward list深度探索

容器array

TR1:C++ TR1(Technical Report 1,技术报告 1)是一个介于 C++03 标准和 C++11 标准之间的阶段性技术报告,旨在为 C++ 标准库引入新功能和库组件提供一种机制。TR1 提供了一些 C++ 标准库的扩展,以及一些新的库组件,作为对 C++03 标准库的增强。

TR1 中的一些建议和组件后来被纳入 C++11 标准,成为了正式的 C++ 标准库的一部分。C++11 标准以及之后的版本中包括了 TR1 中的一些组件,例如 <memory>, <functional>, <random>, <tuple> 等。

这里介绍TR1版本的array实现

在这里插入图片描述

array的用法,需要指定array的大小

array<int, 10> myArray;
auto ite = myArray.begin();
ite += 3;
cout << *ite;

array的实现中没有构造函数和析构函数,GNU C++(g++) 2.9版本中直接拿原生指针当作迭代器, g++2.9版本的vector也是这种实现。

GNU C++ 4.9版本的array

在这里插入图片描述

容器forward_list

单向链表,下面的slide配合list学习。

std::forward_list 是 C++ 标准模板库(STL)中的一种单向链表容器。与 std::list 不同,std::forward_list 是一个单向链表,每个元素只保存指向下一个元素的指针,而不保存指向前一个元素的指针。

以下是一些 std::forward_list 的主要特点和操作:

特点:

  1. 单向链表: std::forward_list 是一个单向链表,每个元素只有指向下一个元素的指针。
  2. 没有 size() 函数: 由于是单向链表,std::forward_list 没有提供直接获取元素个数的 size() 函数。获取元素个数需要遍历整个链表。
  3. 高效插入和删除: 在链表中间插入或删除元素的操作比在其他序列容器中更加高效。
  4. 不支持随机访问: 由于是单向链表,std::forward_list 不支持通过索引直接访问元素。

在这里插入图片描述

18 deque、queue和 stack深度探索(上)

容器deque

std::deque(双端队列,deque 意为 double-ended queue)是 C++ 标准模板库(STL)中的一个容器,它提供了动态数组的功能,允许在两端进行高效的插入和删除操作。以下是关于 std::deque 的一些主要特点和概念:

  1. 双端操作: std::deque 允许在队列的两端(前端和后端)进行高效的插入和删除操作,而不仅仅是在尾部。

  2. 动态数组: std::deque 的内部结构通常是由多个固定大小的块组成的,每个块中存储一定数量的元素。这种结构允许在两端执行常数时间的插入和删除操作,而不需要像动态数组那样移动大量元素。

  3. 迭代器: std::deque 提供随机访问迭代器,可以在常数时间内对其元素进行随机访问。这使得 std::deque 在许多算法和操作中能够高效地使用。

  4. 不要求连续存储:std::vector 不同,std::deque 不要求元素在内存中是连续存储的。这使得在 std::deque 头部和尾部执行插入和删除操作更加高效。

  5. 大小可变: std::deque 的大小可以动态调整,因此它可以根据需要动态分配和释放内存。

总体而言,std::deque 是一个强大的容器,适用于需要在两端进行高效插入和删除操作的情况。它提供了动态数组的灵活性,同时避免了由于频繁在中间插入或删除元素而引起的性能问题。

在这里插入图片描述

deque的内部:

首先有一个map(暂且称为控制中心),里面存储指针,指针指向各个缓冲区buffer(或者叫做node),缓冲区是数组,具体存储数据。缓冲区是连续的,但是不同的缓冲区之间不是连续的,所以称为分段连续。如下图所示,下图中有5个 buffer。

对于下图中的迭代器中的cur,first,last,node四个元素,first和last分别是一个buffer的起始和结束,node是控制中心中哪个buffer的指针,cur指向的是具体的元素值。

所有的容器都维护了两个begin() 和 end(), 就是下图中的迭代器start和finish,它里面也有上述四个元素。

在这里插入图片描述

下面看一下class deque的内容,它里面的数据有:

iterator start; // 16B, 
iterator finish; // 16B
map_pointer map;  // 4B,上面所说的控制中心
size_type map_size;  // 4B, 控制中心这个vector的大小

上面的16 + 16 + 4 + 4 = 40B,这就是一个class deque创建出来的对象本身大小有40B(32位机器上),至于deque里面存储的数据占用的空间大小,是动态分配获得的,和对象本身大小无关。

再看下图的右上角,其中一个模板参数位 BufSiz, 这是GNU C++2.9版本,可以用来指定缓冲区的大小。

在这里插入图片描述

下图是iterator的成员(方框框起来的部分),可以看到map_pointer是 T**,它本身是一个指针,所以cur, first, last, node共计4 * 4B = 16B(在32位电脑上指针是4B大小),这个就是上面iterator的大小。

下图中有

typedef  random_access_iterator_tag iterator_category;

指定迭代器的类型是随机存取的类型,这就是deque对外表现:提供随机访问迭代器,可以在常数时间内对其元素进行随机访问。其实通过上面的学习,我们知道deque底层不是连续的,它是分段连续的。

在这里插入图片描述

下面看deque的insert函数

insert可以在指定位置插入元素,这一页ppt展示的是插入的位置在deque的头或尾,直接调用push_front和push_back,对于中间的插入放到下一页ppt中的insert_aux函数讲。

在这里插入图片描述

下面是insert_aux函数的具体讲解

由于deque是连续的,并且是可以双向扩充的,当插入一个元素(对应安插点)时,就要看一下,哪端元素少,要决定元素的移动是向前还是向后,然后再插入新值,这样速度更快。

在这里插入图片描述

19 deque、queue和 stack深度探索(下)

先看一下deque的函数:

reference front()
{return *start;
}reference back()
{iterator tmp = finish; // 迭代器tmp赋值为finish,finish指向最后一个元素的下一个位置--tmp;// 迭代器--,往前移动一格return *tmp; // 此时返回的是最后一个元素
}size_type size() const
{return finish - start; // -操作符重载// 
}

这里是从源代码中的operator-的重载,摘录如下:

// _Deque_iterator里面的四个值
_Tp* _M_cur;
_Tp* _M_first;
_Tp* _M_last;
_Map_pointer _M_node;difference_type operator-(const _Self& __x) const {return difference_type(_S_buffer_size()) * (_M_node - __x._M_node - 1) +(_M_cur - _M_first) + (__x._M_last - __x._M_cur);
}
// 每个buffer容纳的元素个数,buffer_size
inline size_t __deque_buf_size(size_t __size) {return __size < 512 ? size_t(512 / __size) : size_t(1);
}static size_t _S_buffer_size() { return __deque_buf_size(sizeof(_Tp)); }

在这里插入图片描述

下面看deque如何模拟连续空间,主要是deque iterators的功劳

下面的_M_node - __x._M_node - 1中的_M_node指的是上图中的迭代器中的node节点,_M_node - __x._M_node - 1指的是控制中心两个node相减,得到是首尾buffer之间buffer的个数。(__x._M_last - __x._M_cur)指的是起始buffer的元素个数,就是下图中的97,98,99所在buffer的计算,这个buffer中右侧是元素,左侧是空白(目前还没有存数据)。(_M_cur - _M_first)指的是末尾buffer的元素个数,这个buffer左侧是数据,右侧是空白。

// 两个迭代器之间的距离:指的是多少个元素在两个迭代器之间
difference_type operator-(const _Self& __x) const {return difference_type(_S_buffer_size()) * (_M_node - __x._M_node - 1) +(_M_cur - _M_first) + (__x._M_last - __x._M_cur);
}

在这里插入图片描述

下面看++和–操作符重载:移动一个位置

_Self& operator++() {++_M_cur;if (_M_cur == _M_last) { // 到达一个buffer的边界// _M_node是控制中心的节点_M_set_node(_M_node + 1); // 跳转到下一个buffer_M_cur = _M_first;}return *this; 
}
_Self operator++(int)  {_Self __tmp = *this;++*this;return __tmp;
}_Self& operator--() {if (_M_cur == _M_first) {_M_set_node(_M_node - 1);_M_cur = _M_last;}--_M_cur;return *this;
}
_Self operator--(int) {_Self __tmp = *this;--*this;return __tmp;
}// 从一个buffer跳转到另一个buffer
void _M_set_node(_Map_pointer __new_node) {_M_node = __new_node;_M_first = *__new_node; // 指向新buffer的start_M_last = _M_first + difference_type(_S_buffer_size()); //指向新buffer的last
}

在这里插入图片描述

deque号称是连续空间,迭代器可以跳过多个位置,所以迭代器提供+=的重载

当跳过多个位置跨越了当前buffer的边界,就会到另一个buffer,

_Self& operator+=(difference_type __n)
{difference_type __offset = __n + (_M_cur - _M_first);// 目标位置在同一个缓冲区bufferif (__offset >= 0 && __offset < difference_type(_S_buffer_size()))_M_cur += __n;else { // 目标位置不在同一个缓冲区bufferdifference_type __node_offset =__offset > 0 ? __offset / difference_type(_S_buffer_size()): -difference_type((-__offset - 1) / _S_buffer_size()) - 1;// 切换到正确的buffer_M_set_node(_M_node + __node_offset);// 切换至正确的元素_M_cur = _M_first + (__offset - __node_offset * difference_type(_S_buffer_size()));}return *this;
}_Self operator+(difference_type __n) const
{_Self __tmp = *this;return __tmp += __n;
}

在这里插入图片描述

-=的重载调用上文的+=

  _Self& operator-=(difference_type __n) { return *this += -__n; }_Self operator-(difference_type __n) const {_Self __tmp = *this;return __tmp -= __n;}
// 连续空间需要提供下标操作符[]reference operator[](difference_type __n) const { return *(*this + __n); }

另外这里补充一下this 和 *this的区别:

bool operator==(const _Self& __x) const { return _M_cur == __x._M_cur; }  
bool operator!=(const _Self& __x) const { return !(*this == __x); }

问题:这里的this是什么类型,*this是什么类型?

回答:

在C++中,this 是一个指向当前对象的指针。在成员函数中,this 指针被隐式地传递给成员函数,指向调用该成员函数的对象。因此,this 的类型是指向当前类类型的指针。

在上述代码中,*this 表示当前对象的解引用。由于 this 是指针,*this 就是该指针所指向的对象。因此,*this 的类型是当前类的对象类型。

具体来说,上述代码是一个用于比较不等于的运算符重载函数。*this 是当前对象,__x 是另一个对象。通过 *this == __x 这一比较操作,判断当前对象是否等于另一个对象。!(*this == __x) 表示当前对象不等于另一个对象,因此返回 true。这个重载函数的目的是实现 != 运算符,如果你已经在类中实现了 == 运算符,这个函数就是通过对 == 的结果取反来实现 !=

g++4.9版本的deque

deque的类图如下图所示,也是deque继承自一个deque base,deque base有一个deque impl实现,这个deque impl有一个迭代器deque iterator。

新版本不允许用户指定缓冲区的大小,使用默认值512.

在这里插入图片描述

deque里面有四个成员变量

_M_map // 指向控制中心(这是一个vector)map,控制中心控制一块一块的缓冲区
_M_map_size // 控制中心的大小
// 两个迭代器
_M_start // 指向deque所有元素的头
_M_finish // 指向deque所有元素的尾

这里补充控制中心这个vector扩充的规则:

当vector空间满的时候,会自动两倍扩充空间,并且把旧的元素拷贝进新的两倍空间中,这里实现的时候,拷贝到新vector的中间部分,从而实现vector中间部分不仅可以向后扩张(记录新的缓冲区),而且还可以向前扩张(记录新的缓冲区),如下图右侧,中间控制中心(灰色)。

在这里插入图片描述

容器queue

std::queue 是 C++ 标准模板库(STL)中的一个容器适配器,用于实现先进先出(FIFO)的队列数据结构。它是建立在其他 STL 容器之上的一个封装,通常使用 std::deque 作为默认底层容器。

以下是 std::queue 的一些主要特点和操作:

  1. 先进先出(FIFO): std::queue 实现了队列的基本特性,确保最先进入队列的元素最先被移出。

  2. 容器适配器: std::queue 是一个容器适配器,意味着它不直接提供存储元素的能力,而是通过封装底层容器(默认是 std::deque)来实现队列的行为。

  3. 底层容器: 默认情况下,std::queue 使用 std::deque 作为底层容器,但你也可以通过模板参数来指定其他底层容器,比如 std::liststd::vector

  4. 入队和出队操作: std::queue 提供了 push 方法用于将元素入队,pop 方法用于将队首元素出队。

    std::queue<int> myQueue;
    myQueue.push(1);
    myQueue.push(2);
    myQueue.pop();
    
  5. 队首和队尾访问: front 方法返回队首元素的引用,back 方法返回队尾元素的引用。

    std::queue<int> myQueue;
    int frontElement = myQueue.front();  // 访问队首元素
    int backElement = myQueue.back();    // 访问队尾元素
    
  6. 大小和空判断: size 方法返回队列中的元素数量,empty 方法检查队列是否为空。

    std::queue<int> myQueue;
    bool isEmpty = myQueue.empty();  // 检查队列是否为空
    

std::queue 提供了一种方便的方式来操作队列,尤其是在需要实现先进先出的场景下。它隐藏了底层容器的实现细节,使得队列的使用更加简单。

queue的源代码如下图,它调用deque中的各种方法实现自己的功能

在这里插入图片描述

容器stack

std::stack 是 C++ 标准模板库(STL)中的一个容器适配器,用于实现后进先出(LIFO)的栈数据结构。它是建立在其他 STL 容器之上的一个封装,通常使用 std::deque 作为默认底层容器。

以下是 std::stack 的一些主要特点和操作:

  1. 后进先出(LIFO): std::stack 实现了栈的基本特性,确保最后入栈的元素最先被弹出。

  2. 容器适配器: std::stack 是一个容器适配器,意味着它不直接提供存储元素的能力,而是通过封装底层容器(默认是 std::deque)来实现栈的行为。

  3. 底层容器: 默认情况下,std::stack 使用 std::deque 作为底层容器,但你也可以通过模板参数来指定其他底层容器,比如 std::liststd::vector

  4. 入栈和出栈操作: push 方法用于将元素入栈,pop 方法用于将栈顶元素出栈。

    std::stack<int> myStack;
    myStack.push(1);
    myStack.push(2);
    myStack.pop();
    
  5. 栈顶访问: top 方法返回栈顶元素的引用。

    std::stack<int> myStack;
    int topElement = myStack.top();  // 访问栈顶元素
    
  6. 大小和空判断: size 方法返回栈中的元素数量,empty 方法检查栈是否为空。

    std::stack<int> myStack;
    bool isEmpty = myStack.empty();  // 检查栈是否为空
    

std::stack 提供了一种方便的方式来操作栈,尤其是在需要实现后进先出的场景下。它隐藏了底层容器的实现细节,使得栈的使用更加简单。

下面是stack的源代码,它也是调用deque的各个函数实现自己的功能

在这里插入图片描述

queue和stack,关于其iterator和底层结构

stack和queue都不允许遍历,不提供iterator

stack和queue都可以选择list或者deque作为底层结构,如下图所示,便是使用list作为底层结构

stack<string, list<string>> c;
queue<string, list<string>> c1;

在这里插入图片描述

stack可以选择vector作为底层结构

queue不能选择vector作为底层结构

在这里插入图片描述

stack和queue都不能选择set或map做底层结构

在这里插入图片描述

后记

截至2024年1月7日,学习完list, vector, deque, queue, stack的底层实现,接下来要学习基于红黑树的容器。

相关文章:

STL标准库与泛型编程(侯捷)笔记3

STL标准库与泛型编程&#xff08;侯捷&#xff09; 本文是学习笔记&#xff0c;仅供个人学习使用。如有侵权&#xff0c;请联系删除。 参考链接 Youbute: 侯捷-STL标准库与泛型编程 B站: 侯捷 - STL Github:STL源码剖析中源码 https://github.com/SilverMaple/STLSourceCo…...

Iceberg: 列式读取Parquet数据

通过Spark读取Parquet文件的基本流程 SQL > Spark解析SQL生成逻辑计划树 LogicalPlan > Spark创建扫描表/读取数据的逻辑计划结点 DataSourceV2ScanRelation > Spark优化逻辑计划树&#xff0c;生成物理计划树 SparkPlan > Spark根据不同的属性&#xff0c;将逻辑…...

Ansible、Saltstack、Puppet自动化运维工具介绍

本文主要是分享介绍三款主流批量操控工具Ansible、Saltstack、Puppet主要对比区别&#xff0c;以及Ansible和saltstack的基础安装和使用示例&#xff0c;如果觉得本文对你有帮助&#xff0c;欢迎点赞、收藏、评论&#xff01; There are many things that can not be broken&am…...

python线程池提交任务

1. 线程池参数设置 CPU数量&#xff1a;N线程池的核心线程数量 IO密集型的话&#xff0c;一般设置为 2 * N 1&#xff1b; CPU密集型的话&#xff0c;一般设置为 N 1 或者 使用进程池。线程池的最大任务队列长度 &#xff08;线程池的核心线程数 / 单个任务的执行时间&#…...

跨境电商企业客户服务优化指南:关键步骤与实用建议

随着全球经济一体化的加强&#xff0c;跨境电子商务产业在过去几年蓬勃发展。但是&#xff0c;为应对激烈竞争&#xff0c;提供全方面的客户服务成为了跨境电子商务卖家在市场中获得优势的关键因素之一。本文将介绍跨境电商企业优化客户服务有哪些步骤&#xff1f;以助力企业提…...

Visual Studio Code 常用快捷键

Visual Studio Code 常用快捷键 文章目录 Visual Studio Code 常用快捷键1. 主命令框2. 常用快捷键2.1 编辑器与窗口管理2.2 代码编辑格式调整光标相关重构代码查找替换显示相关其他 1. 主命令框 F1 或 CtrlShiftP : 打开命令面板。在打开的输入框内&#xff0c;可以输入任何命…...

ubuntu创建pytorch-gpu的docker环境

文章目录 安装docker创建镜像创建容器 合作推广&#xff0c;分享一个人工智能学习网站。计划系统性学习的同学可以了解下&#xff0c;点击助力博主脱贫( •̀ ω •́ )✧ 使用docker的好处就是可以将你的环境和别人的分开&#xff0c;特别是共用的情况下。本文介绍了ubuntu环境…...

数据库原理与应用期末复习试卷2

数据库原理技术与应用 一.单项选择题 设有属性A&#xff0c;B&#xff0c;C&#xff0c;D&#xff0c;以下表示中不是关系的是( C) ​ A、R(A) B、R(A, B, C, D) C、R&#xff08;AxBxCxD&#xff09; D、R(A&#xff0c;B) 在SQL语言中的视图VIEW是数据库的(A&#xff09;…...

操作系统丨单元测试

文章目录 单元测试选择题填空题单元测试 选择题 【单选题】可以实现虚拟存储器的方案是(D)。 A. 固定分区方式 B. 可变分区方式 C. 纯分页方式 D. 请求页式 【单选题】文件系统中文件存储空间的分配是以(D)为基本单位进行的。 A. 字 B. 字节 C. 文件 D. 块 【单选题】哪种…...

tcp/ip协议2实现的插图,数据结构6 (24 - 章)

(142) 142 二四1 TCP传输控制协议 tcpstat统计量与tcp 函数调用链 (143) 143 二四2 TCP传输控制协议 宏定义与常量值–上 (144) 144 二四3 TCP传输控制协议 宏定义与常量值–下 (145) 145 二四4 TCP传输控制协议 结构tcphdr,tcpiphdr (146) 146 二四5 TCP传输控制协议 结构 tcp…...

Linux链接的创建,删除,修改

目录 1. 概述2. 硬链接2.1 创建硬链接2.2 删除硬链接 3. 软链接3.1 创建软链接3.2 删除软链接 5. 常用的终端工具下载 计算机基础–Linux详解 1. 概述 在Linux系统中&#xff0c;链接是一种文件系统中的重要概念。链接允许用户在文件系统中创建指向另一个文件的引用&#xff0c…...

HarmoryOS Ability页面的生命周期

接入穿山甲SDK app示例&#xff1a; android 数独小游戏 经典数独休闲益智 广告接入示例: Android 个人开发者如何接入广告SDK&#xff0c;实现app流量变现 Ability页面的生命周期 学习前端&#xff0c;第一步最重要的是要理解&#xff0c;页面启动和不同场景下的生命周期的…...

【Flink 从入门到成神系列 一】算子

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;阿里巴巴淘天Java开发工程师&#xff0c;CSDN博客专家&#x1f4d5;系列专栏&#xff1a;Spring源码、Netty源码、Kafka源码、JUC源码、dubbo源码系列&#x1f525;如果感觉博主的文章还不错…...

无人机自主寻优降落在移动车辆

针对无人机寻找并降落在移动车辆上的问题&#xff0c;一套可能的研究总体方案&#xff1a; 问题定义与建模&#xff1a; 确定研究的具体范围和目标&#xff0c;包括无人机的初始条件、最大飞行距离、允许的最大追踪误差等。建立马尔科夫决策过程模型&#xff08;MDP&#xff09…...

科技感十足界面模板

科技感界面 在强调简洁的科技类产品相关设计中&#xff0c;背景多数分为&#xff1a;颜色或写实图片两种。 颜色很好理解&#xff0c;大多以深色底为主。强调一种神秘感和沉稳感&#xff0c;同时可以和浅色的文字内容形成很好的对比。 而图片背景的使用&#xff0c;就要求其…...

pytest装饰器 @pytest.mark.parametrize 使用方法

pytest.mark.parametrize 有三种传参方法&#xff0c;分别是&#xff1a; 1.列表传参&#xff1a;将参数值作为列表传递给装饰器。 pytest.mark.parametrize("param", [value1, value2, ..., valuen])2.元组传参&#xff1a;将参数值作为元组传递给装饰器。 pytes…...

redis被攻击

之前由于redis没有修改端口&#xff0c;密码也比较简单&#xff0c;也没有绑定ip 结果被攻击了 1 redis里被写入string类型的脚本&#xff0c;比如&#xff1a;Back1 Back2 Back3 Back4 &#xff0c;内容curl -fsSL http://d.powerofwish.com/pm.sh | sh的形式&#xff0c;如下…...

二手买卖、废品回收小程序 在app.json中声明permission scope.userLocation字段 教程说明

处理二手买卖、废品回收小程序 在app.json中声明permission scope.userLocation字段 教程说明 sitemapLocation 指明 sitemap.json 的位置&#xff1b;默认为 ‘sitemap.json’ 即在 app.json 同级目录下名字的 sitemap.json 文件 找到app.json这个文件 把这段代码加进去&…...

【AI视野·今日Sound 声学论文速览 第四十期】Wed, 3 Jan 2024

AI视野今日CS.Sound 声学论文速览 Wed, 3 Jan 2024 Totally 4 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers Auffusion: Leveraging the Power of Diffusion and Large Language Models for Text-to-Audio Generation Authors Jinlong Xue, Yayue De…...

Unity组件开发--升降梯

我开发的升降梯由三个部分组成&#xff0c;反正适用于我的需求了&#xff0c;其他人想复用到自己的项目的话&#xff0c;不一定。写的也不是很好&#xff0c;感觉搞的有点复杂啦。完全可以在优化一下&#xff0c;项目赶工期&#xff0c;就先这样吧。能用就行&#xff0c;其他的…...

插槽slot涉及到的样式污染问题

1. 前言 本次我们主要结合一些案例研究一下vue的插槽中样式污染问题。在这篇文章中&#xff0c;我们主要关注以下两点: 父组件的样式是否会影响子组件的样式&#xff1f;子组件的样式是否会影响父组件定义的插槽部分的样式&#xff1f; 2. 准备代码 2.1 父组件代码 <te…...

OpenCV-Python(25):Hough直线变换

目标 理解霍夫变换的概念学习如何在一张图片中检测直线学习函数cv2.HoughLines()和cv2.HoughLinesP() 原理 霍夫变换在检测各种形状的的技术中非常流行。如果你要检测的形状可以用数学表达式写出来&#xff0c;你就可以是使用霍夫变换检测它。即使检测的形状存在一点破坏或者…...

python接口自动化(七)--状态码详解对照表(详解)

1.简介 我们为啥要了解状态码&#xff0c;从它的作用&#xff0c;就不言而喻了。如果不了解&#xff0c;我们就会像个无头苍蝇&#xff0c;横冲直撞。遇到问题也不知道从何处入手&#xff0c;就是想找别人帮忙&#xff0c;也不知道是找前端还是后端的工程师。 状态码的作用是&a…...

Android 实现动态申请各项权限

在Android应用中&#xff0c;如果需要使用一些敏感的权限&#xff08;例如相机、位置等&#xff09;&#xff0c;需要经过用户的授权才能访问。在Android 6.0&#xff08;API级别23&#xff09;及以上的版本中&#xff0c;引入了动态权限申请机制。以下是在Android应用中实现动…...

【leetcode】力扣热门之合并两个有序列表【简单难度】

题目描述 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 用例 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 输入&#xff1a;l1 [], l2 [] 输出&#xff1a;[] 输入&#xff1a;l1 []…...

安全与认证Week3 Tutorial+历年题补充

目录 1) 什么是重放攻击? 2)什么是Kerberos系统?它提供什么安全服务? 3)服务器验证客户端身份的一种简单方法是要求提供密码。在Kerberos中不使用这种身份验证&#xff0c;为什么?Kerberos如何对服务器和客户机进行身份验证? 4) Kerberos的四个要求是什么?Kerberos系…...

【Kotlin】协程

Kotlin协程 背景定义实践GlobalScope.launchrunBlocking业务实践 背景 在项目实践过程中&#xff0c;笔者发现很多异步或者耗时的操作&#xff0c;都使用了Kotlin中的协程&#xff0c;所以特地研究了一番。 定义 关于协程&#xff08;Coroutine&#xff09;&#xff0c;其实…...

Scikit-Learn线性回归(五)

Scikit-Learn线性回归五:岭回归与Lasso回归 1、误差与模型复杂度2、范数与正则化2.1、范数2.2、正则化3、Scikit-Learn Ridge回归(岭回归)4、Scikit-Learn Lasso回归1、误差与模型复杂度 在第二篇文章 Scikit-Learn线性回归(二) 中,我们已经给出了过拟合与模型泛化的概念并…...

React(2): 使用 html2canvas 生成图片

使用 html2canvas 生成图片 需求 将所需的内容生成图片div 中包括 svg 等 前置准备 "react": "^18.2.0","react-dom": "^18.2.0","html2canvas": "^1.4.1",实现 <div ref{payRef}></div>const pa…...

CAN物理层协议介绍

目录 ​编辑 1. CAN协议简介 2. CAN物理层 3. 通讯节点 4. 差分信号 5. CAN协议中的差分信号 1. CAN协议简介 CAN是控制器局域网络(Controller Area Network)的简称,它是由研发和生产汽车电子产品著称的德国BOSCH公司开发的,并最终成为国际标准(ISO11519) &#xff0…...

西昌市做网站的/搜索引擎营销特点是什么

【转】全球10大黑客组织排行榜 转自&#xff1a;http://www.hackbase.com/article-213542-1.html 在过去的几年中,涌现出了大量的黑客组织,他们发展成员的速度已经达到了惊人的程度。原先那种只针对个人互联网用户的攻击,现在已经演变成为,黑客间相互配 合的团体攻击。攻击目标…...

验证码平台 wordpress/大数据营销

org.springframework.dao.InvalidDataAccessResourceUsageException 写了一个SpringBoot测试类&#xff0c;想测试一下Repository是不是写对了&#xff0c;结果出了这个错误&#xff0c;看了好几篇文章&#xff0c;有好几个解决办法都试了但是没有用&#xff0c;然后看到一条回…...

推广类电商文案/小红书seo是什么

说到编码&#xff0c;你会想到什么&#xff1f;你想到的可能是gb2312、gbk、utf-8、unicode、ansi、ascii&#xff0c;你还会想到项目中常常碰到的汉字乱码问题&#xff0c;编辑器里面的汉字便乱码了&#xff1f;浏览器便乱码了&#xff1f;怎么回事&#xff1f;是不是一通捣鼓…...

淘宝上做淘宝客的网站/google关键词优化

平时关注的一些技术博客&#xff08;排名不分先后&#xff09;&#xff1a; 阮一峰的网络日志&#xff1a;ES6 这份国内基本互联网公司&#xff1a;https://www.cnblogs.com/IT-Bear/p/5566506.html 张鑫旭&#xff1a;重点关注css技术 刘未鹏&#xff1a;关于时间管理与技术学…...

网站流量站怎么做的/谷歌seo服务公司

LAMP 系统性能调优之内核调优措施 2011-03-18 11:21 Sean A. Walberg 网络转载 字号&#xff1a;T | T在对系统的 Apache、PHP 和 MySQL 组件进行调优之前&#xff0c;应该花一些时间确保底层 Linux 组件的运行正常。这点是非常重要的&#xff01; AD&#xff1a;2014WOT全球软…...

楚雄做网站的公司/企业网站怎么推广

Here&#xff1a;https://pan.baidu.com/s/1EKKJ0elmJ1M0NpxPzsenKA 转载于:https://www.cnblogs.com/guoyangfan/p/11369966.html...