C++:STL之Vector实现

news/2024/4/26 7:53:58/文章来源:https://blog.csdn.net/myscratch/article/details/128065750

vector各函数

#include<iostream>
#include<vector>
using namespace std;namespace lz
{//模拟实现vectortemplate<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//默认成员函数vector();                                           //构造函数vector(size_t n, const T& val);                     //构造函数template<class InputIterator>vector(InputIterator first, InputIterator last);    //构造函数vector(const vector<T>& v);                         //拷贝构造函数vector<T>& operator=(const vector<T>& v);           //赋值运算符重载函数~vector();                                          //析构函数//迭代器相关函数iterator begin();iterator end();const_iterator begin()const;const_iterator end()const;//容量和大小相关函数size_t size()const;size_t capacity()const;void reserve(size_t n);void resize(size_t n, const T& val = T());bool empty()const;//修改容器内容相关函数void push_back(const T& x);void pop_back();void insert(iterator pos, const T& x);iterator erase(iterator pos);void swap(vector<T>& v);//访问容器相关函数T& operator[](size_t i);const T& operator[](size_t i)const;private:iterator _start;        //指向容器的头iterator _finish;       //指向有效数据的尾iterator _endofstorage; //指向容器的尾};
}

分析:

  1. 为什么需要模板?
      答:该类内部可以的成员变量类型可以是任意,所以需要使用类模板,此外类内所有涉及val的函数参数类型都是T。
  • 关于模板爆了的错:
    在类内声明类外实现,::必须加temeplate<class T>
    请添加图片描述
    ::class T不行,会报错,模板声明完,搭配的是vector<T>
    请添加图片描述
  1. 成员变量:
      答:我们常用[]去访问vector,vector空间是连续的。一般都需要通过迭代器,本质是原生指针。因为存储元素的不确定性,所以定义指针的类型也是T* 。最开始做了:typedef T* iterator。_start、尾元素下一位(并未存储):_finish、最多能放到的位置:_endofstorage-1。
  2. 关于:size()和capacity():
      答:跟上一节string不一样,我们没有对应容量属性,size()直接用finish-start(比如10个元素,我们从1号到10号,finish是尾的下一个,在11号,11-1=10,10是size,所以该式子正确)。而容量:_endofstorage-star是容量,因为数组从0开始放。_finish == _endof,考虑扩容。因为已经超了这最后一个可放位置。
  3. 关于构造函数:
      答:这里有两种构造函数,分别是无参,有两个参(int n,const T &val=T())。半缺省型构造函数。
  • 半缺省型构造函数:vector(size_t n, const T& val = T());
    分析:
      如果第二个参数给值,我们就取引用,不给值就用默认类型。解释T=(),如果是string,就调string的默认构造函数。显然我们常用vector的这个函数知道,比如我们用bool类型,自动填充的是false。总之,这个的意思就是,如果不给值,我们就根据val本身的类型,去通过构造函数填充默认值。此外,例如:int a(10),这样是可以通过编译的。
  • 带初始化n个 值的拷贝构造函数:vector(size_t n, const T& val)
    参数类型:后面的T()是说val给默认值。调用默认构造函数。
    实现过程:
    构造参数列表中完成初始化。
    函数体内:扩容、循环push_back插值使得每个位置有值。
    **经典错误:**这里不慎可能有错,如下图:
    请添加图片描述
    用10个1初始化会报错
    请添加图片描述
    但是char,就不会报错。
    请添加图片描述
    分析:一个参数没问题。第一个参数是int,第二个是char也没问题。而两个int,就出错了。当给一个参数时,半缺省构造函数最匹配,就会调用半缺省构造函数。int、char会调用半缺省。int符合size_t,const T,可以变成char。而用int和int,size_t转换一次匹配上了,const T再转换一次,所以跟半缺省情况需要转换两次。而如果和下面inputIterator做匹配,只需要转换一次,所以下面更匹配,然而下面的函数体不能让代码通过,所以就报错了。那么如果改半缺省为构造函数第一个参数类型为int,原码中本身是size_t(无符号整型)。
    请添加图片描述
    解决方案:
      我看的视频里面说,给个重载即可,一个int类型重载。
  1. 拷贝构造函数:
      答:在类外实现,只是加前缀vector::拷贝构造函数名,后面别变。
    1> 传统写法过程:
    开辟连续空间,可以开capacity()也可以开size()大小。
    拷贝数据内容,挨个拷贝,以start[i]=v[i]赋值式接收做深拷贝
    更新_finish和_endOfS,更新_finfish依据size()方法。而 更新endOfS必须和开空间的大小方法一致。至此,可以做如下定义:v2(v1)
    2>
    总之,主要注意点就是不能用memcpy(),要做深拷贝
    3> 现代写法过程:
    先reserve(n),更新了内部成员变量。再像上面一样,做挨个深拷贝。
    这里深拷贝可以用:push_back(),push_bac()的实现其实是直接给最后一个位置写值,且是引用类型,如果是对象类型会做深拷贝。使得每个插入赋值做的是深拷贝。
    4> 更简洁
    做构造函数做tmp,
    使用自写swap(),自写swap原理是:交换_start,因为底层是连续空间,交换_start值即可。这里只是涉及指针变化,但是使得拷贝构造函数中实现深拷贝借助这个没问题。因为连续空间方便直接访问。
  2. 迭代器构造函数
    1.>写迭代器构造函数需模板声明,因为如果类型写成iterator模板,那么迭代器构造只能是该类型的迭代器。然而,该vector应该要支持各种迭代器类型的迭代器构造函数。类内可以再用模板。因为外面的迭代器本质是char*,但是你外面调用不同的类型做出的实例化后得到的模板类的类型不一样。总之,使用模板让你可以用STL中各种迭代器类型去初始化构造vector
    2,> 如下代码:我们发现可以用string的迭代器去使vector中的迭代器构造函数去拷贝构造一个vector,如果你输出发现结果是string每个字符的ascall码。但是你用vector就可以输出每个字符本身。
string s("hh aa);
lz::vector<int> v(s.begin(), s.end());
for(auto e:v)
{cout << e << endl;
}

2.> 分析过程:调用时,使用last和first两个不同迭代器类型,也就是vector的首尾位置。所以我们拿到这两个变量,做加法,first最终会到last。
3.> 注意一定要先在构造列表中做初始化,因为后面reserve()中会使用到这些值。
4.>由此,可以再用迭代器构造函数的方式写拷贝构造函数。先创建一个以迭代器构造函数拷贝一个vector<>tmp,再用自己实现的Swap()使得当前对象成员变量与tmp交换,更加省事。

  1. 析构函数:
      答:成员变量迭代器类型,也就是指针。直接释放即可。且置于nullptr。释放_start即可,因为连续。
  2. push_back():
      答:参数必须为const T&x,一定要加const。首先用&效率高,其次,经常push_back(“xxx”),显然参数是个常量字符串,而常量字符串用单参数引用接收用隐式类型转换,产生临时变量,临时变量具有常性。且简单看,T类型是普通类型肯定不能接收常量类型。
    回忆:隐式类型转换:因为用其它类型接收某一类型
double d = 2.2;
int& i = d;

有编译错误,因为d会隐式类型转换。
请添加图片描述
  再回到这个函数上,需要先扩容判断,当endofsotrage == _finish,就reverse()。
9. 关于容量的函数:

  • reserve():
    基本思路:如果n>capacity()才做扩容,如果符合,再判断之前是否存在元素,如果存在还要把旧的拷贝过来。开辟空间:T* tmp = new T[n];
    易错点: 1. 扩容且复制完后,如果用三行代码去规整结构:
_start = tmp;
_finish = _start + size();
_end_of_s = _start + n;

经典错误1.:size():return _finish - _start; start已经通过扩容后新空间的首地址tmp更新了,那么size()返回的size已经用坏味道了。那么怎么办呢?可以在更新_start之前先求size();
因为==_finish在末元素下 一个,整体从0开始,所以+n,到末元素后一==,思路正确。而==_endofs = start + n 也是在最大合法位置的下一个位置==。因整体从0开始,所以它下标是N,然而是第N+1个位置。所以每次扩容时_finish == +endOfs时做扩容。
经典错误2:memcpy():memcpy只适合浅拷贝。

  • resize():开空间加初始化。reserve()不会改变size。
    麻烦点在如果已经有空间了。
    过程:如果n > _capacity 需要扩容加初始化。reserve(n)。
    然后之间初始化,n>size():就执行:拷贝从_finish到 start + n位置做*_finish = val。 ++_finish;
    如果在size和capacity之间,说明有空间,只需要初始化填数据。如果小于size,改变size即可。n <= size,是做缩小,_finish = start+n;n个元素,而_finish是末尾的下一个,所以赋值start + n。只改变大小_size。
  1. 重载:
  • []:size_t pos:因为要修改内容,所以需要T& 模板引用类型。此外,位置要合法
  • = :赋值重载
    我没有实现重载方法,在vs2022中都不能通过编译,这个错误很典型。但能引发它的原因可能有很多。如下因为没实现赋值重载,需要的函数声明了,但没实现
    请添加图片描述
    赋值重载过程:

1>简洁写法:通过非引用类型的参数做了拷贝构造,再利用Swap()直接交换参数很当前对象。
2> 参数类型:vector类型,返回值是vector类型。返回当前对象本身。return *this;赋值运算符显然要的是个对象。

  1. 迭代器:
    迭代器必须public,私有的typedef无法访问。因为外部迭代器类型,需要定义出来使用。此外,我在类内声明而类外实现iteartor会有错。如下图:
    请添加图片描述
      需要加typename标志后面加的是一个类型。为什么要加这个呢?C->W,W类型是C,W是C的从属名称。W类型并不明确,如图片里面的C::iterator,是模板参数中其它的值,类型不明确,编译器可能编译有歧义,不知道它是类型还是什么函数,所以需要你用typename来做标志,标记后面是个类型。当类外声明迭代器时,需要我们去加上typename此外,常说的类模板是具体实例化后的类,比如:vector<int>,这样它才算模板类
    在这里插入图片描述
    在这里插入图片描述
  • const类型迭代器
    需要重命名一个const类型,且再写begin()和end(),类型是const_iteartor,参数类型在()后加const。

解释地址:
https://blog.csdn.net/qq_40080842/article/details/127037413?app_version=5.11.1&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22127037413%22%2C%22source%22%3A%22qq_40080842%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app

此外,++it,没有重载++,it = begin(),也能从返回的位置往后挪到,因为it是int*地址类型,地址++,默认移动一个存储元素大小,所以往后挪一个int。

  1. CRUD
  • insert()
    1,> 返回值和参数:不需要返回值,参数是:size_t pos , const T& x ,内部存模板参数类型的值。
    1.>先判断位置是否合法,pos <=_finish,允许等于,做尾插。>_start。
    2.> 判断是否需要扩容,_findish == _endofS,_endOfS位置在N+1,然而最大能放N,原始赋值endOfS = _start + n;
    3.> 挪动数据,iteartor end = _finish - 1;
    挪动范围: end >= pos,意思是从后往前pos位置的也被挪走。
    挪动习惯:*(end+1 )= *(end); 这样使得变量最小为0,不会越界。再–end。
      观察如下使用场景:
    请添加图片描述
    p通过find()查找,但是insert()内部如果发生了扩容,那么从start~endOfS之间内存地址都变了,你的p已经非法了,所以实现时,reserve()之前,要先记录原始len,再更新pos = start + len。
    以上,就是一个迭代器失效问题,迭代器失效还会常见于erase()
      此外,再对p做插入,还会报错,虽然内部对pos更新了,但是没有影响到p。因为内部pos是形参,所以建议:实现使用引用做参数。但是尊重源码实现,源码参数不是引用,此外就算给了引用,也没用,因为迭代器begin()返回也不是引用类型,是临时对象,但是如果给迭代器的begin()变成&,所以建议不重复对一个位置做插入

  • pop_back()
    1> 参数是const T&,直接给末尾赋值,本质做深拷贝
    因为底层是连续空间,直接–_finish;表示末尾的下一个往回收。****

  • insert()
    1.> 过程:
    先判断pos位置是否合法 ,在start~endfS
    判断是否需要扩容,需要先保存原始长度len,再去reserve(n),此时内部的_start已经改变,需要手动计算扩容后的pos真实位置。
    拷贝原始数据,不要用memcpy(),当内存类型为对象类型,则需要做深拷贝memcpy()只适合默认类型的浅拷贝时使用。
    所以这里实现时:_start[i] = v[i];

  • erase()
    注意类外实现都需要加上typename,防止歧义,告诉编译器:这是个类型。
    1.>过程:
    先查位置是否合法两个assert。
    当前位置于pos+1,然后以pos-1 = pos去循环赋值。
    最后给_finish–。
    还可以加缩容判断,当size()为capacity()一半,就缩容。
    2.>注意点:不要重复对一个位置删,因为删完做了挪动,当前迭代器位置可能已经变了,可能会跳过一些元素,最终可能报错
    3.>解决思路:删除完返回迭代器位置,且删除过程中加判断,if(符合删除条件){调用erase()} else{it++;}
      此外,STL规定erase()返回删除位置的下一个。建议insert和erase之后别访问,可能有各种迭代器失效情况

  1. 其它功能:
  • find():其实是algorithm中的sort()
    自己写的类是否可以用算法库中的sort(),可以用,只有符合迭代器的规范。
  1. 动态二维数组:使数组支持二维
  • 如何保证代码支持二维vector?
      先实现取数组头尾的函数:front()、back()。这两个函数的类型是:引用&类型,因为需要修改。如果本身存二维,那么front()结果是一维数组,return _start,就是个一维,因为堆二维解引用一次。back()同理,解引用一次,但是是对(_finish-1)。
      分析:vv里面每个数据类型是:vector,把二维的vv返回给ret。首先,ret会去调用拷贝构造,ret的_start会新开辟空间,做深拷贝。然后开辟的空间内部。其中拷贝构造过程中的插值用push_back,push_back做的是深拷贝,深拷贝一个一维vector,而这个一维vector,插入一维vector过程中,也做深拷贝。总之,尤其是拷贝构造,一定要做深拷贝。且reserve过程中,拷贝也保证了不用memcpy。一个个去复制。start[i] = v][i]。或用push_back();

头文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS#include<iostream>
#include<assert.h>
#include<algorithm>
using namespace std;namespace lz
{//模拟实现vectortemplate<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//默认成员函数vector();                                           //构造函数vector(size_t n, const T& val);                     //构造函数template<class InputIterator>vector(InputIterator first, InputIterator last)    //构造函数:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){while (first != last){push_back(*first);first++;}}vector(const vector<T>& v);                       //拷贝构造函数vector<T>& operator=(vector<T> v);           //赋值运算符重载函数~vector();                                          //析构函数//迭代器相关函数iterator begin();iterator end();const_iterator begin()const;const_iterator end()const;//容量和大小相关函数size_t size()const;size_t capacity()const;void reserve(size_t n);void resize(size_t n, const T& val = T()){if (n > capacity()){reserve(n);}if (n > size()){// 初始化填值while (_finish < _start + n){*_finish = val;++_finish;}}else_finish = _start + n;}bool empty()const;//修改容器内容相关函数void push_back(const T& x);void pop_back();void insert(iterator pos, const T& x);iterator erase(iterator pos);//访问容器相关函数T& operator[](size_t i);const T& operator[](size_t i)const;// 其它功能void swap(vector<T>& v){std::swap(v._start, _start);std::swap(v._finish, _finish);std::swap(v._endofstorage, _endofstorage);}T& front();T& back();private:iterator _start;        //指向容器的头iterator _finish;       //指向有效数据的尾iterator _endofstorage; //指向容器的尾};//========构造函数template<class T>vector<T>::vector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr){};//vector<class T>::vector() //{//	// 2 12//};//template<class T>//vector<T>::vector(const vector<T>& v)//	:_start(nullptr)//	, _finish(nullptr)//	, _endofstorage(nullptr)//{//	_start = new T[v.capacity()]; //开辟一块和容器v大小相同的空间//	for (size_t i = 0; i < v.size(); i++) //将容器v当中的数据一个个拷贝过来//	{//		_start[i] = v[i];//	}//	_finish = _start + v.size(); //容器有效数据的尾//	_endofstorage = _start + v.capacity(); //整个容器的尾//	//}// 拷贝构造现代写法//template<class T>//vector<T>::vector(const vector<T>& v)//	:_start(nullptr)//	, _finish(nullptr)//	, _endofstorage(nullptr)//{//	reserve(v.capacity()); //调用reserve函数将容器容量设置为与v相同//	for (auto& e : v) //将容器v当中的数据一个个尾插过来//	{//		push_back(e);//	}//}//// v2(v) 拷贝构造 现代 简洁写法template<class T>vector<T>::vector(const vector<T>& v):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){vector<T> tmp(v.begin(), v.end());swap(tmp);}template<class T>vector<T>::vector(size_t n, const T& val):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n); //调用reserve函数将容器容量设置为nfor (size_t i = 0; i < n; i++) //尾插n个值为val的数据到容器当中{push_back(val);}}template<class T>vector<T>:: ~vector(){delete[] _start; //释放容器存储数据的空间_start = nullptr; //_start置空_finish = nullptr; //_finish置空_endofstorage = nullptr;}//========容量相关// 扩容template<class T>void vector<T>::reserve(size_t n){if (n > capacity()) //判断是否需要进行操作{size_t sz = size(); //记录当前容器当中有效数据的个数T* tmp = new T[n]; //开辟一块可以容纳n个数据的空间if (_start) //判断是否为空容器{for (size_t i = 0; i < sz; i++) //将容器当中的数据一个个拷贝到tmp当中{tmp[i] = _start[i];}delete[] _start; //将容器本身存储数据的空间释放}_start = tmp; //将tmp所维护的数据交给_start进行维护_finish = _start + sz; //容器有效数据的尾_endofstorage = _start + n; //整个容器的尾}}/*template<class T>void vector<T>::resize(size_t n, const T& val = T()){}*/template<class T>size_t vector<T>::capacity()const{return _endofstorage - _start;}template<class T>size_t vector<T>::size()const{return _finish - _start;}template<class T>bool vector<T>::empty()const{return size() == 0;}//========CRUDtemplate<class T>void vector<T>::push_back(const T& x){if (_finish == _endofstorage) //判断是否需要增容{size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity(); //将容量扩大为原来的两倍reserve(newcapacity); //增容}*_finish = x; //尾插数据_finish++; //_finish指针后移}template<class T>void vector<T>::pop_back(){assert(!empty()); //容器为空则断言_finish--; //_finish指针前移}template<class T>void vector<T>::insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage) //判断是否需要增容{size_t len = pos - _start; //记录pos与_start之间的间隔size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity(); //将容量扩大为原来的两倍reserve(newcapacity); //增容pos = _start + len; //通过len找到pos在增容后的容器当中的位置}//将pos位置及其之后的数据统一向后挪动一位,以留出pos位置进行插入iterator end = _finish-1;while (end >= pos){*(end+1) = *(end);end--;}*pos = x; //将数据插入到pos位置_finish++; //数据个数增加一个,_finish后移}//template<class T>//typename vector<T>::iterator vector<T>::erase(typename vector<T>::iterator pos)//{//	assert(pos>= _start);//	assert(pos < _finish);//	iterator it = pos + 1;//	while (it != _finish)//	{//		*(it - 1) = *it;//		it++;//	}//	_finish--; //数据个数减少一个,_finish前移//	return pos;//}template<class T>typename vector<T>::iterator vector<T>::erase(typename vector<T>::iterator pos){assert(pos>= _start);assert(pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;it++;}_finish--; //数据个数减少一个,_finish前移return pos;}//========重载template<class T>T& vector<T>::operator[](size_t i){assert(i < size()); //检测下标的合法性return _start[i]; //返回对应数据}template<class T>const T& vector<T>::operator[](size_t i)const{assert(i < size()); //检测下标的合法性return _start[i]; //返回对应数据}template<class T>vector<T>& vector<T>::operator=(vector<T> i){swap(i);return *this;}//========迭代器template<class T>typename vector<T>::iterator vector<T>::begin(){return  _start;}template<class T>typename vector<T>::iterator vector<T>::end(){return _finish;}template<class T>typename vector<T>::const_iterator vector<T>::begin()const{return  _start;}template<class T>typename vector<T>::const_iterator vector<T>::end()const{return _finish;}template<class T>T& vector<T>::front(){assert(size() > 0);return *_start;}template<class T>T& vector<T>::back(){assert(size() > 0);return *(_finish - 1);}class Solution {public:vector<vector<int>> generate(int numRows) {// resize()初始化 开几行vector<vector<int>> res;res.resize(numRows);// 每一行再次初始化开空间,i行开i+1个// 默认给0 但是初始化第一个和最后一个给1for (int i = 0; i < numRows; i++){res[i].resize(i + 1, 0);res[i].front() = res[i].back() = 1;}// 行列for (int i = 0; i < numRows; i++){for (int j = 0; j < res[i].size(); j++){if (res[i][j] == 0)res[i][j] = res[i - 1][j - 1] + res[i - 1][j];cout << "i = "<< i <<" , j = " << j <<" , res = " << res[i][j] << endl;}}return res;}};
}

测试文件

#include"l2vector_t.h"void test_v1()
{lz::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;
}void test_iteartor()
{lz::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;lz::vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}}void test_insert()
{lz::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);for (size_t i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;auto p = find(v.begin(), v.end(), 3);if (p != v.end()){v.insert(p, 30);}for (auto e : v){cout << e << " " << endl;}}void test_erase()
{lz::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);auto p = find(v.begin(), v.end(), 3);if(p!=v.end()){v.erase(p);}for (auto e : v){cout << e << " ";}cout << endl;}void test_copy()
{lz::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);lz::vector<int> v2(v);for (auto& e : v){e = 1;cout << e << " ";}cout << endl;for (auto e : v2){cout << e << " ";}}void test_vector_iterator()
{string s("hello world");lz::vector<char> v(s.begin(), s.end());for (auto e : v){cout << e << " ";}cout << endl;
}// 赋值重载的测试
void test_operator_equals()
{lz::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);lz::vector<int> v2;v2 = v;for (auto e : v){cout << e << " ";}
}
void test_resize()
{lz::vector<int> v;v.resize(10, 1);for (auto e : v){cout << e << " ";}cout << endl;v.resize(3);for (auto e : v){cout << e << " ";}
}void test_vectorerwei()
{lz::Solution().generate(5);
}
int main()
{//test_iteartor();//test_insert();//test_erase();//test_copy();//test_vector_iterator();//test_operator_equals();//test_resize();test_vectorerwei();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_39009.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Netty进阶——粘包与半包(代码示例)

目录一、消息粘包和消息半包的概述1.1、消息粘包1.2、消息半包二、粘包现象代码示例2.1、粘包现象服务端示例代码2.2、粘包现象客户端示例代码2.3、分别启动服务端&#xff0c;客户端&#xff0c;查看服务端结果输出三、半包现象代码示例3.1、半包现象服务端示例代码3.2、半包现…

【强化学习论文】小样本策略泛化的提示决策转换器

文献题目&#xff1a;Prompting Decision Transformer for Few-Shot Policy Generalization 摘要 人类可以利用先前的经验并从少量演示中学习新任务。与旨在通过更好的算法设计实现快速适应的离线元强化学习相比&#xff0c;我们研究了架构归纳偏差对少样本学习能力的影响。我…

不懂单链表? 这篇文章就帮你搞明白

坚持看完&#xff0c;结尾有思维导图总结 链表对指针的操作要求不低链表的概念链表的特性链表的功能(最重要)定义和初始化头插头删细节说明尾插尾删寻找链表元素与打印链表在 某位置后插入删除在某位置的插入删除销毁链表链表的概念 什么是链表 官方概念&#xff1a;链表是一种…

显卡---显卡驱动---CUDA---Cudnn

1. 背景 最近在follow百度的CAE这篇论文时&#xff0c;源码需要的环境为&#xff1a; python 3.7 cuda: 11.0 cudnn: 8.0.4 gcc 8.2 该版本要求与我目前使用的服务器上的CUDA版本不相符合。因此搜索了一篇国外小哥的文章&#xff0c;讲述了如何在一台服务器上安装多个CUDA和Cud…

Node之Express学习笔记

一.Express 1.1什么是Express Express的作用和Node.js内置的http模块类似 专门用来创建Web服务器的 本质&#xff1a;npm上的第三方包&#xff0c;提供快速创建Web服务器的便捷方法 1.2Express能做什么 Web网站服务器&#xff1a;专门提供Web网页资源的服务器 API接口服务器&…

FPGA----ZCU106基于axi-hp通道的pl与ps数据交互(全网唯一最详)

1、大家好&#xff0c;今天给大家带来的内容是&#xff0c;基于AXI4协议的采用AXI-HP通道完成PL侧数据发送至PS侧&#xff08;PS侧数据发送至PL侧并没有实现&#xff0c;但是保留了PL读取PS测数据的接口&#xff09; 2、如果大家用到SoC这种高级功能&#xff0c;那大家应该对于…

Linux进阶-进程间通信(ipc)

进程间通信&#xff1a;数据传输、资源共享、事件通知、进程控制。 Linux系统下的ipc 早期unix系统 ipc&#xff1a;管道&#xff08;数据传输&#xff09;、信号&#xff08;事件通知&#xff09;、fifo&#xff08;数据传输&#xff09;。 system-v ipc&#xff08;贝尔实…

Kubernetes之Pod初始化容器

Kubernetes之Pod初始化容器 概述 ​ 初始化是很多编程语言普遍关注的问题&#xff0c;甚至有些编程语言直接支持模式构造来生成初始化程序&#xff0c;这些用于进行初始化的程序结构称为初始化器或初始化列表。初始化代码要首先运行&#xff0c;且只能运行一次&#xff0c;它们…

CAPM资产定价模型

本文是Quantitative Methods and Analysis: Pairs Trading此书的读书笔记。 CAPM(Capital Asset Pricing Model)资产定价模型 这个模型在金融界的影响就是beta这个词的使用。CAPM模型用两个部分的回报之和来解释一个资产的回报。其中一个部分是指市场&#xff08;称为market)…

时间序列:时间序列模型---自回归过程(AutoRegressive Process)

本文是Quantitative Methods and Analysis: Pairs Trading此书的读书笔记。 这次我们构造一个由无限的白噪声实现&#xff08;white noise realization) 组成的时间序列&#xff0c;即。这个由无限数目的项组成的值却是一个有限的值&#xff0c;比如时刻的值为&#xff0c; 而…

Magic Leap 2设计和开发幕后花絮

Magic Leap今年发布新款AR头显Magic Leap 2&#xff0c;相比于上一代Magic Leap 1&#xff0c;新品更专注于B端场景&#xff0c;自公布以来&#xff0c;Magic Leap不仅对公司策略、理念更加透明&#xff0c;也不断公开ML2产品设计背后的思考。相比于ML1&#xff0c;ML2的设计有…

粒子群算法查找函数最小值

实现 函数 F01、F06 的优化测试 以下内容是现有算法的运行结果、调参分析、及代码实现&#xff0c;用于给其他人提供参考&#xff0c;懒得改了hh 1. 运行结果 参数 w 0.5 &#xff08;可更改&#xff09; c1 2.0 &#xff08;可更改&#xff09; c2 2.0 &#xff08;可更改&…

Echart 柱状图,X轴斜着展示

option { color: [‘#3398DB’], tooltip: { trigger: ‘axis’, axisPointer: { // 坐标轴指示器&#xff0c;坐标轴触发有效 type: ‘shadow’ // 默认为直线&#xff0c;可选为&#xff1a;‘line’ | ‘shadow’ } }, grid: { left: ‘3%’, right: ‘4%’, bottom: ‘3%’…

iPhone升级iOS 16后出现提示“面容ID不可用”怎么办?

最近&#xff0c;很多用户在苹果社区反馈&#xff0c;iPhone升级iOS 16后Face ID不能用了&#xff0c;尝试重置Face ID时&#xff0c;系统会弹窗提示“面容ID不可用&#xff0c;稍后尝试设置面容ID。” 如果你的iPhone在没有摔落手机或是手机进水的情况下出现这个弹窗&#xff…

【uniapp小程序】路由跳转navigator传参封装

文章目录&#x1f34d;前言&#x1f34b;正文1、看官网1.1 navigator API 介绍1.2、路由跳转参数传递1.3、五种常见的跳转方式1.3.1 uni.navigateTo(OBJECT)1.3.2 uni.redirectTo(OBJECT)1.3.3 uni.reLaunch(OBJECT)1.3.4 uni.switchTab(OBJECT)1.3.5 uni.navigateBack(OBJECT)…

图的初识·基本概念

文章目录基本概念图有两种基本形式无向图的表示有向图的表示基本概念 图结构也是数据结构的一部分。而且还有一点小难。图是由多个结点链接而成的&#xff0c;但是一个结点可以同时连接多个其他结点&#xff0c;多个节点也可以同时指向一个节点。【多对多的关系】 图结构是任意…

如何从报表控件FastReport .NET中连接到 PostgreSQL 数据库?

FastReport.NET官方版下载 Fastreport是目前世界上主流的图表控件&#xff0c;具有超高性价比&#xff0c;以更具成本优势的价格&#xff0c;便能提供功能齐全的报表解决方案&#xff0c;连续三年蝉联全球文档创建组件和库的“ Top 50 Publishers”奖。 慧都科技是Fast Repor…

mysql索引类别和失效场景

首先&#xff0c;我们为什么要使用索引&#xff0c;索引有什么作用呢&#xff1f; 索引可以用来快速查询数据表中有某一特定值的记录&#xff0c;大大加快数据的查询速度&#xff1b;在列上创建了索引之后&#xff0c;查找数据时可以直接根据该列上的索引找到对应记录行的位置…

YOLO X 改进详解

YOLO X 主要改进&#xff1a; Anchor-Free: FCOSDecoupled detection headAdvanced label assigning strategy Network structure improvement Decoupled detection head 对比于YOLO V5, YOLO X 在detection head上有了改进。YOLO V5中&#xff0c;检测头是通过卷积同时预…

视频编解码 - RTP 与 RTCP

目录 RTP 实时传输协议 RTCP协议 将H264 RTP打包 RTP 实时传输协议 音视频数据传输&#xff0c;先将原始数据经过编码压缩后&#xff0c;将码流打包成一个个RTP包&#xff0c;再将码流传输到接收端。 打包的作用 接收端要正确地使用这些音视频编码数据&#xff0c;不仅仅需…