【STL】vector模拟实现

vector模拟实现

  • 一、vector函数接口总览
  • 二、vector当中的成员介绍
  • 三、list模拟实现
    • 1、默认成员函数
      • (1)构造函数1
      • (2)构造函数2
      • (3)构造函数3
    • 2、拷贝构造函数
      • (1)写法一:老式写法
      • (2)写法二:现代写法
    • 3、赋值运算符重载函数
      • (1)写法一:传统写法
      • (2)写法二:现代写法
    • 4、析构函数
  • 四、迭代器相关函数
    • 1、begin和end
    • 2、const对象的begin和end
    • 3、使用演示
  • 五、容器和大小相关函数
    • 1、size和capacity
    • 2、reserve
      • 细节问题1、在进行操作之前需要提前记录当前容器当中有效数据的个数。
      • 细节问题2、拷贝容器当中的数据时,不能使用memcpy函数进行拷贝。
    • 3、resize
    • 4、empty
  • 六、修改容器内的相关函数
    • 1、push_back
    • 2、pop_back
    • 3、insert
    • 4、erase
    • 5、swap
  • 七、访问容器相关函数
    • 1、operator[]
    • 2、const operator[]
  • 八、代码总览


一、vector函数接口总览

namespace JRH
{// vectortemplate<class T>class vector{public:// 1、默认的函数vector(); // 构造函数1vector(size_t n, const T& val); // 构造函数2template<class InputIterator>vector(InputIterator first, InputIterator last); // 构造函数3vector(const vector<T>& vt); // 拷贝构造vector<T>& operator=(const vector<T>& vt); // 运算符赋值重载函数~vector(); // 析构函数// 2、迭代器相关函数iterator begin(); // 头iterator end(); // 尾const_iterator begin() const; // const头const_iterator end() const; // const尾// 3、容器相关的函数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; // 判空// 4、修改容器内容相关函数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);// 5、访问容器相关函数T& operator[](size_t i);const T& operator[](size_t i)const;private:iterator _start;        //指向容器的头iterator _finish;       //指向有效数据的尾iterator _endofstorage; //指向容器的尾};
}

二、vector当中的成员介绍

在vector当中有三个成员变量_start、_finish、_endofstorage。

在这里插入图片描述

_start指向容器的头,_finish指向容器当中有效数据的尾,_endofstorage指向整个容器的尾。

三、list模拟实现

1、默认成员函数

(1)构造函数1

首先支持一个无参的构造函数,我们只需要将其三个变量都定为nullptr即可。

		vector() // 构造函数1:_start(nullptr), _finish(nullptr), _endofstorge(nullptr){}

(2)构造函数2

vector还支持构造这样一种容器,该容器当中含有n个值为val的数据。对于该构造函数,我们可以先使用reserve函数将容器容量先设置为n,然后使用push_back函数尾插n个值为val的数据到容器当中即可。

		vector(size_t n, const T& val) // 构造函数2:_start(nullptr),_finish(nullptr),_endofstorage(nullptr){reserve(n); // 调用下面写的reverse函数将其空间先定义为n个// 尾插n个val值进入到vector中for (size_t i = 0; i < n; i++){push_back(val);}}

注意

  1. 这个reverse函数有大用,我们要开好刚好的n个的空间,因为避免调用push_back函数时需要增容多次,导致效率降低。
  2. 该构造函数还需要实现两个重载函数(int和long)。
		vector(int n, const T& val) // 构造函数2 -- 重载int:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n); // 调用下面写的reverse函数将其空间先定义为n个// 尾插n个val值进入到vector中for (int i = 0; i < n; i++){push_back(val);}}vector(long n, const T& val) // 构造函数2 -- 重载long:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n); // 调用下面写的reverse函数将其空间先定义为n个// 尾插n个val值进入到vector中for (long i = 0; i < n; i++){push_back(val);}}

(3)构造函数3

vector还支持使用一段迭代器区间进行对象的构造。因为该迭代器区间可以是其他容器的迭代器区间,也就是说该函数接收到的迭代器的类型是不确定的,所以我们这里需要将该构造函数设计为一个函数模板,在函数体内将该迭代器区间的数据一个个尾插到容器当中即可

		template<class InputIterator>vector(InputIterator first, InputIterator last) // 构造函数3:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){// 将迭代器中的所有区间的值全部都放进vector中while (first != last){// this->push_back(*first);push_back(*first);first++;}}

2、拷贝构造函数

(1)写法一:老式写法

先开辟一块与该容器大小相同的空间,然后将该容器当中的数据一个个拷贝过来即可,最后更新_finish和_endofstorage的值即可。

		vector(const vector<T>& vt) // 拷贝构造:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){// 先开一块相同大小的空间_start = new T[vt.capacity()];for (size_t i = 0; i < vt.size(); i++) // 一个个拷贝过去{_start[i] = vt[i];}_finish = _start + vt.size(); // 有效数据的尾_endofstorage = _start + vt.capacity(); // 整个数组的尾}

注意: 将容器当中的数据一个个拷贝过来时不能使用memcpy函数,当vector存储的数据是内置类型或无需进行深拷贝的自定义类型时,使用memcpy函数是没什么问题的,但当vector存储的数据是需要进行深拷贝的自定义类型时,使用memcpy函数的弊端就体现出来了。例如,当vector存储的数据是string类的时候。

在这里插入图片描述
并且vector当中存储的每一个string都指向自己所存储的字符串。
在这里插入图片描述

如果此时我们使用的是memcpy函数进行拷贝构造的话,那么拷贝构造出来的vector当中存储的每个string的成员变量的值,将与被拷贝的vector当中存储的每个string的成员变量的值相同,即两个vector当中的每个对应的string成员都指向同一个字符串空间。

在这里插入图片描述

这结果是肯定错误的,因为都指向同一块空间了,假如销毁原字符串用memcpy拷贝构造的函数也销毁了,那么我们看一下代码是怎么实现的:
在这里插入图片描述

代码中看似是使用普通的“=”将容器当中的数据一个个拷贝过来,实际上是调用了所存元素的赋值运算符重载函数,而string类的赋值运算符重载函数就是深拷贝,所以拷贝结果是这样的:

在这里插入图片描述

  • 如果vector当中存储的元素类型是内置类型(int)或浅拷贝的自定义类型(Date),使用memcpy函数进行进行拷贝构造是没问题的,但如果vector当中存储的元素类型是深拷贝的自定义类型(string),则使用memcpy函数将不能达到我们想要的效果。

(2)写法二:现代写法

现代写法比较简单且暴力,直接一个for循环将其一个个放到this指针的push_back中即可,也就是一个个尾插过来即可。
在使用范围for对容器v进行遍历的过程中,变量e就是每一个数据的拷贝,然后将e尾插到构造出来的容器当中。就算容器v当中存储的数据是string类,在e拷贝时也会自动调用string的拷贝构造(深拷贝),所以也能够避免出现与使用memcpy时类似的问题。

		vector(const vector<T>& vt) // 拷贝构造 -- 现代写法:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(vt.capacity()); // 开辟一段vt等大的空间for (auto e : vt){push_back(e); // 将容器vt中的数据一个个尾插进来}}

3、赋值运算符重载函数

(1)写法一:传统写法

首先判断是否是给自己赋值,若是给自己赋值则无需进行操作。若不是给自己赋值,则先开辟一块和容器v大小相同的空间,然后将容器v当中的数据一个个拷贝过来,最后更新_finish和_endofstorage的值即可
同样也不能用memcpy函数进行拷贝,其原理与上面的拷贝构造函数是一样的。

		vector<T>& operator=(const vector<T>& vt) // 运算符赋值重载函数 -- 传统写法{if (this != &vt) // 不能自己给自己赋值{delete[] _start; // 释放原来的空间_start = new T[vt.capacity()]; // 开辟一块与vt等大的空间for (size_t i = 0; i < vt.size(); i++){_start[i] = vt[i]; // 将vt里面的数据一个个拷贝过来}_finish = _start + vt.size(); // 有效数据的大小_endofstorage = _start + vt.capacity(); // 整个数组的大小}return *this; // 支持连续赋值}

(2)写法二:现代写法

首先在右值传参时并没有使用引用传参,因为这样可以间接调用vector的拷贝构造函数,然后将这个拷贝构造出来的容器v与左值进行交换,此时就相当于完成了赋值操作,而容器v会在该函数调用结束时自动析构。

赋值运算符重载的现代写法也是进行的深拷贝,只不过是调用的vector的拷贝构造函数进行的深拷贝,在赋值运算符重载函数当中仅仅是将深拷贝出来的对象与左值进行了交换而已。

		vector<T>& operator=(const vector<T> vt) // 运算符赋值重载函数 -- 现代写法{std::swap(vt); // 交换这两个值return *this; // 支持连续赋值}

4、析构函数

析构函数很简单,但有细节,是析构函数首先要判断是不是空指针,不可释放空指针,非空指针后先释放原空间,再将三个变量置空即可。

		~vector() // 析构函数{if (_start != nullptr) // 避免对空指针进行曹组{delete[] _start; // 释放空间_start = nullptr; _finish = nullptr;_endofstorage = nullptr;}}

四、迭代器相关函数

我们的迭代器就是容器当中所存储数据类型的指针,我们首先定义两个迭代器,一个是iterator,另一个是const_iterator,我们看一下下面的定义:

		typedef T* iterator;typedef const T* const_iterator;

1、begin和end

begin()就是返回该vector的首元素的地址,end()返回容器当中有效数据的下一个数据的地址。我们看一下下面的代码:

		iterator begin() // 头{return _start; // 返回头元素地址}iterator end() // 尾{return _finish; // 返回有效数据的下一个元素地址}

2、const对象的begin和end

const对象的begin和end函数是和上面一样的,只不过重载了上面的两个函数,我们直接显示代码即可:

		const_iterator begin() const // const头{return _start; // 返回头元素地址}const_iterator end() const // const尾{return _finish; // 返回有效数据的下一个元素地址}

3、使用演示

第一种我们可以用迭代器iterator来进行演示:

		for (size_t i = 0; i < v.size(); ++i){std::cout << v[i] << " ";}std::cout << std::endl;

第二种我们用for循环来进行演示,因为for循环可以用的情况下也就是底层的是迭代器也可以进行使用。

		for (auto e : v){std::cout << e << " ";}std::cout << std::endl;

五、容器和大小相关函数

1、size和capacity

vector当中三个成员遍历各自的指向,我们可以很容易得出当前容器中的有效数据个数和最大容量。
在这里插入图片描述
size()就是已用空间的大小,也就是_finish-_start;capacity()就是总空间的大小,也就是_endofstorage-_start。

		size_t size() const // 大小{return _finish - _start;}size_t capacity() const // 容量{return _endofstorage - _start;}

2、reserve

  1. 当n大于对象当前的capacity时,将capacity扩大到n或大于n。
  2. 当n小于对象当前的capacity时,什么也不做。

reserve函数的实现思路也是很简单的,先判断所给n是否大于当前容器的最大容量(否则无需进行任何操作),操作时直接开辟一块可以容纳n个数据的空间,然后将原容器当中的有效数据拷贝到该空间,之后将原容器存储数据的空间释放,并将新开辟的空间交给该容器维护,最好更新容器当中各个成员变量的值即可。

		void reserve(size_t n) // 保持容量{if (n > capacity()) // 判断是否需要进行操作{size_t sz = size(); // 保存一下有效数据的个数T* tmp = new T[n]; // 开辟一个可以容纳n个空间的tmpif (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i]; // 将原本容器中的所有数据都拷贝到tmp临时空间中}delete[] _start; // 释放原空间}_start = tmp; // 将tmp临时空间中的数据全都放到对象容器当中_finish = _start + sz; // 有效数据个数_endofstorage = _start + n; // 总容量大小}}

细节问题1、在进行操作之前需要提前记录当前容器当中有效数据的个数。

我们在进入循环记录了一下有效数据的大小sz,这是因为我们最后需要更新_finish指针的指向,而_finish指针的指向就等于_start指针加容器当中有效数据的个数,当_start指针的指向改变后我们再调用size函数通过_finish - _start计算出的有效数据的个数就是一个随机值了。

在这里插入图片描述

细节问题2、拷贝容器当中的数据时,不能使用memcpy函数进行拷贝。

可能你会想,当vector当中存储的是string的时候,虽然使用memcpy函数reserve出来的容器与原容器当中每个对应的string成员都指向同一个字符串空间,但是原容器存储数据的空间不是已经被释放了,相当于现在只有一个容器维护这这些字符串空间,这还有什么影响。
但是不要忘了,当你释放原容器空间的时候,原容器当中存储的每个string在释放时会调用string的析构函数,将其指向的字符串也进行释放,所以使用memcpy函数reserve出来的容器当中的每一个string所指向的字符串实际上是一块已经被释放的空间,访问该容器时就是对内存空间进行非法访问。
在这里插入图片描述

所以还是用for循环和=将其一个个赋值过来才是王道:
在这里插入图片描述

3、resize

  1. 当n大于当前的size时,将size扩大到n,扩大的数据为val,若val未给出,则默认为容器所存储类型的默认构造函数所构造出来的值。
  2. 当n小于当前的size时,将size缩小到n。

根据resize函数的规则,进入函数我们可以先判断所给n是否小于容器当前的size,若小于,则通过改变_finish的指向,直接将容器的size缩小到n即可,否则先判断该容器是否需要增容,然后再将扩大的数据赋值为val即可。

		void resize(size_t n, const T& val = T()// 模板缺省值) // 扩容{if (n < size()) // 当n小于当前的size时{_finish = _start + n; // 将size缩小到n}else // 当n大于当前的size时{if (n > capacity()) // 判断是否需要增容{reserve(n);}while (_finish < _start + n) // 将size扩大到n{*_finish = val;_finish++;}}}

4、empty

只需要比较_start和_finish的位置是否相同即可,相同则空了,不相同则非空。

		bool empty() const // 判空{return _start == _finish;}

六、修改容器内的相关函数

1、push_back

简单,判断是否容器已经满了,满了就扩容,然后将数据尾插到_finish指向的位置,再将_finish++即可。

		void push_back(const T& x){if (_finish == _endofstorage) // 到容量底了{reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;}

2、pop_back

简单,先进行判空,这里用assert断言即可,方便快捷,非空则直接将_finish--即可。

		void pop_back(){assert(!empty());--_finish;}

3、insert

insert函数可以在所给迭代器pos位置插入数据,在插入数据前先判断是否需要增容,然后将pos位置及其之后的数据统一向后挪动一位,以留出pos位置进行插入,最后将数据插入到pos位置即可。

		void insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start; // 记录一下相差大小// 扩容reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1; // 将pos位置及其之后的数据统一向后挪动一位,以留出pos位置进行插入while (end >= pos){*(end + 1) = *end;--end;}*pos = x; // 插入 ++_finish;  // 往后移一位}

4、erase

erase函数可以删除所给迭代器pos位置的数据,在删除数据前需要判断容器释放为空,若为空则需做断言处理,删除数据时直接将pos位置之后的数据统一向前挪动一位,将pos位置的数据覆盖即可。

		iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator start = pos + 1;// 将pos位置之后的数据统一向前挪动一位,以覆盖pos位置的数据while (start != _finish){*(start - 1) = *start;++start;}--_finish;}

5、swap

直接用std库中的swap函数即可,用其进行交换三个变量,这里的std::域作用限定符告诉编译器这里优先在全局范围寻找swap函数,否则编译器会认为你调用的就是你正在实现的swap函数(就近原则)。

		void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}

七、访问容器相关函数

只有一个operator[]来进行访问,实现时直接返回对应位置的数据即可。

1、operator[]

		T& operator[](size_t i){assert(i < size());return _start[i];}

2、const operator[]

		const T& operator[](size_t i) const{assert(i < size());return _start[i];}

这里的权限只需要读,不能写。

八、代码总览

stl_vector.h:

#include<iostream>
#include<cassert>namespace JRH
{// vectortemplate<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;public:// 1、默认的函数vector() // 构造函数1:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){}vector(size_t n, const T& val) // 构造函数2:_start(nullptr),_finish(nullptr),_endofstorage(nullptr){reserve(n); // 调用下面写的reverse函数将其空间先定义为n个// 尾插n个val值进入到vector中for (size_t i = 0; i < n; i++){push_back(val);}}vector(int n, const T& val) // 构造函数2 -- 重载int:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n); // 调用下面写的reverse函数将其空间先定义为n个// 尾插n个val值进入到vector中for (int i = 0; i < n; i++){push_back(val);}}vector(long n, const T& val) // 构造函数2 -- 重载long:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(n); // 调用下面写的reverse函数将其空间先定义为n个// 尾插n个val值进入到vector中for (long i = 0; i < n; i++){push_back(val);}}template<class InputIterator>vector(InputIterator first, InputIterator last) // 构造函数3:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){// 将迭代器中的所有区间的值全部都放进vector中while (first != last){// this->push_back(*first);push_back(*first);first++;}}vector(const vector<T>& vt) // 拷贝构造 -- 传统写法:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){// 先开一块相同大小的空间_start = new T[vt.capacity()];for (size_t i = 0; i < vt.size(); i++) // 一个个拷贝过去{_start[i] = vt[i];}_finish = _start + vt.size(); // 有效数据的尾_endofstorage = _start + vt.capacity(); // 整个数组的尾}//vector(const vector<T>& vt) // 拷贝构造 -- 现代写法//	:_start(nullptr)//	, _finish(nullptr)//	, _endofstorage(nullptr)//{//	reserve(vt.capacity()); // 开辟一段vt等大的空间//	for (auto e : vt)//	{//		push_back(e); // 将容器vt中的数据一个个尾插进来//	}//}vector<T>& operator=(const vector<T>& vt) // 运算符赋值重载函数 -- 传统写法{if (this != &vt) // 不能自己给自己赋值{delete[] _start; // 释放原来的空间_start = new T[vt.capacity()]; // 开辟一块与vt等大的空间for (size_t i = 0; i < vt.size(); i++){_start[i] = vt[i]; // 将vt里面的数据一个个拷贝过来}_finish = _start + vt.size(); // 有效数据的大小_endofstorage = _start + vt.capacity(); // 整个数组的大小}return *this; // 支持连续赋值}//vector<T>& operator=(const vector<T> vt) // 运算符赋值重载函数 -- 现代写法//{//	std::swap(vt); // 交换这两个值//	return *this; // 支持连续赋值//}~vector() // 析构函数{if (_start != nullptr) // 避免对空指针进行曹组{delete[] _start; // 释放空间_start = nullptr; _finish = nullptr;_endofstorage = nullptr;}}// 2、迭代器相关函数iterator begin() // 头{return _start; // 返回头元素地址}iterator end() // 尾{return _finish; // 返回有效数据的下一个元素地址}const_iterator begin() const // const头{return _start; // 返回头元素地址}const_iterator end() const // const尾{return _finish; // 返回有效数据的下一个元素地址}// 3、容器相关的函数size_t size() const // 大小{return _finish - _start;}size_t capacity() const // 容量{return _endofstorage - _start;}void reserve(size_t n) // 保持容量{if (n > capacity()) // 判断是否需要进行操作{size_t sz = size(); // 保存一下有效数据的个数T* tmp = new T[n]; // 开辟一个可以容纳n个空间的tmpif (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i]; // 将原本容器中的所有数据都拷贝到tmp临时空间中}delete[] _start; // 释放原空间}_start = tmp; // 将tmp临时空间中的数据全都放到对象容器当中_finish = _start + sz; // 有效数据个数_endofstorage = _start + n; // 总容量大小}}void resize(size_t n, const T& val = T()) // 扩容{if (n < size()) // 当n小于当前的size时{_finish = _start + n; // 将size缩小到n}else // 当n大于当前的size时{if (n > capacity()) // 判断是否需要增容{reserve(n);}while (_finish < _start + n) // 将size扩大到n{*_finish = val;_finish++;}}}bool empty() const // 判空{return _start == _finish;}// 4、修改容器内容相关函数void push_back(const T& x){if (_finish == _endofstorage) // 到容量底了{reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;}void pop_back(){assert(!empty());--_finish;}void insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start; // 记录一下相差大小// 扩容reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1; // 将pos位置及其之后的数据统一向后挪动一位,以留出pos位置进行插入while (end >= pos){*(end + 1) = *end;--end;}*pos = x; // 插入 ++_finish;  // 往后移一位}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator start = pos + 1;// 将pos位置之后的数据统一向前挪动一位,以覆盖pos位置的数据while (start != _finish){*(start - 1) = *start;++start;}--_finish;}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}// 5、访问容器相关函数T& operator[](size_t i){assert(i < size());return _start[i];}const T& operator[](size_t i) const{assert(i < size());return _start[i];}private:iterator _start;        //指向容器的头iterator _finish;       //指向有效数据的尾iterator _endofstorage; //指向容器的尾};void test_vector1(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.pop_back();//v.pop_back();for (size_t i = 0; i < v.size(); ++i){std::cout << v[i] << " ";}std::cout << std::endl;for (auto e : v){std::cout << e << " ";}std::cout << std::endl;vector<int>::iterator it = v.begin();while (it != v.end()){std::cout << *it << " ";it++;}std::cout << std::endl;v.resize(2);std::cout << v.capacity() << std::endl;std::cout << v.size() << std::endl;}
}

main.cc:

#include"stl_vector.h"int main()
{JRH::test_vector1();return 0;
}

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

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

相关文章

键盘重映射禁用 CtrlAltDel 键的利弊

目录 前言 一、Scancode Map 的规范 二、禁用 CtrlAltDel 的方法及其缺陷 三、编程实现和测试 3.1 C 实现的简易修改工具 3.2 C# 实现的窗口工具 四、总结 本文属于原创文章&#xff0c;转载请注明出处&#xff1a; https://blog.csdn.net/qq_59075481/article/details…

龙芯开启ssh服务——使用Putty连接

本文采用龙芯3A6000处理器&#xff0c;Loongnix操作系统。 为了能使用其他电脑远程操控龙芯电脑&#xff0c;需要打开loongnix的ssh服务&#xff0c;并在其他电脑里使用putty连接loongnix。 1 修改ssh配置文件 命令行输入&#xff1a; sudo vim /etc/ssh/sshd_config按下i插…

Linux-进程信号

Linux进程信号 初步认识信号信号的存储结构信号的处理方式信号的产生硬件异常产生的信号核心转储sigset_t信号集信号集的操作函数对block表的操作对pending表的操作对handler表的操作信号的捕捉用户态和内核态 信号的处理过程可重入函数volatile关键字 初步认识信号 生活中有哪…

LeetCode、136. 只出现一次的数字【简单,位运算】

文章目录 前言LeetCode、136. 只出现一次的数字【简单&#xff0c;位运算】题目链接与分类思路异或一遍运算 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术…

冲击可以通过峭度指标来检测,是如何来检测的,python示例代码

问题 冲击可以通过峭度指标来检测&#xff0c;是如何来检测的&#xff0c;可以给1个示例代码吗 思路 带冲击的信号其峭度指标>3不带冲击的信号其峭度指标在3左右可以通过滑动窗来检测在哪一段 示例代码 带冲击的信号峭度指标值 import numpy as np import matplotlib.…

2024.02.12

使用STM32Cubemx创建一个工程并且给出每一步的含 选择芯片创建工程 开启调试功能 配置时钟 配置时间树 工程管理 配置仿真器

幻兽帕鲁服务器原来的存档不想玩了,怎么清档?如何重来?

如果需要备份原存档的话&#xff0c;就先把存档导出来备份。或者手动去服务器文件里找到游戏存档文件夹&#xff0c;保存下载。 如无需备份原存档&#xff0c;则可以直接使用幻兽帕鲁应用模板&#xff0c;来重装服务器的操作系统。 方法很简单&#xff1a; 详细教程地…

odoo封装字段widget中无感知刷新数据

常规操作是直接刷新页面 window.location.reload(); 深入源码&#xff0c;发现页面controller层有reload 对应tree的字段widget this.__owl__.parent.parentWidget.__parentedParent.reload(); 对应form的字段widget this.__parentedParent.__parentedParent.reload()

APP inventor零基础移动应用开发

1.Android平台简介 Android由谷歌和开放手机联盟共同创建的一款针对手机的开源软件工具包 主要特色 ---开放性 – 丰富的硬件选择 – 开发商不受任何限制 – 无缝集成互联网服务 App Inventor是由Google公司开发的一款在线开放的Android编程工具软件&#xff0c;通过图形化…

huggingface学习|用dreambooth和lora对stable diffusion模型进行微调

目录 用dreambooth对stable-diffusion-v1-5模型进行微调&#xff08;一&#xff09;模型下载和环境配置&#xff08;二&#xff09;数据集准备&#xff08;三&#xff09;模型微调&#xff08;四&#xff09;运行微调后的模型 用lora对stable-diffusion-v1-5模型进行微调&#…

【C语言】动态内存深入了解(一口气刨根问底学完系列,全乎,建议三连点赞收藏)

目录 1.动态内存分配的原因 2.动态内存函数的介绍 2.1malloc和free函数 2.2calloc函数 2.3realloc函数 3. 常见的动态内存错误 3.1 对NULL指针的解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 使用free释放一块动态开辟内存的一部…

Swift 初见

Swift 初见 学习swift的记录 控制台输出 print("hello world")可以无分号&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 变量声明 let 常量声明 如果在常量声明后再次修改他会报错 var 变量声明 一个常量的值&#xf…

linux应用 进程间通信之共享内存(POSIX)

1、前言 1.1 定义 POSIX共享内存是一种在UNIX和类UNIX系统上可用的进程间通信机制。它允许多个进程共享同一块内存区域&#xff0c;从而可以在这块共享内存上进行读写操作。 1.2 应用场景 POSIX共享内存适用于需要高效地进行大量数据交换的场景&#xff0c;比如多个进程需要…

upload-labs文件上传漏洞靶场

第一关 <?php eval ($_POST[123]);?>发现他这个是通过客户端前端写了一个限制 我们禁用srcipt即可 蚁剑成功打开 第二关 我们上传文件2.php它提示我们文件类型不正确 我们可以联想到做了后缀检测 我们通过burp抓包修改后缀 第三关 我们上传一个.php文件不可上…

Peter算法小课堂—区间模型

Peter Pan来啦…… 最大不重叠区间数 二话不说&#xff0c;先来一道题 大家想想怎么贪心&#xff1f;我们可以将每一个美食摊位抽象成一个区间&#xff0c;区间左端点为开始排队时间&#xff0c;右端点为结束排队时间。其中&#xff0c;时间信息可以用数轴表示。 额……我们…

【分布式技术专题】「Zookeeper中间件」Paxos协议的原理和实际运行中的应用流程分析

Paxo算法介绍 Paxos算法是莱斯利兰伯特(Leslie Lamport)1990年提出的一种基于消息传递的一致性算法。 Paxos产生背景 Paxos算法是基于消息传递且具有高度容错特性的一致性算法&#xff0c;是目前公认的解决分布式一致性问题最有效的算法之一&#xff0c;其解决的问题就是在分…

轮播图 HarmonyOS 鸿蒙 ArkTS ArkUI

第一步&#xff1a;新建图片数组 State swiperimgs:Array<Object>[$r(app.media.a), //本地图片或者网络图片$r(app.media.b),$r(app.media.c),$r(app.media.d)] 第二步&#xff1a;写入轮播图代码 Column(){Swiper(){ForEach(this.swiperimgs, (item) > {Image(item…

【c语言】字符串常见函数 上

&#x1f388;个人主页&#xff1a;甜美的江 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;c语言 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&a…

【JVM篇】怎么解决内存泄漏问题

文章目录 &#x1f50e;什么是内存泄漏&#x1f6f8;解决内存泄漏⭐发现问题⭐诊断原因⭐修复问题 &#x1f50e;什么是内存泄漏 在Java中如果不再使用一个对象&#xff0c;但是这个对象仍然在GC Root的引用链上&#xff0c;这个对象就不会被垃圾回收器回收&#xff0c;这种情…

openGauss学习笔记-218 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-I/O

文章目录 openGauss学习笔记-218 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-I/O218.1 查看I/O状况218.2 性能参数分析 openGauss学习笔记-218 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-I/O 获取openGauss节点的CPU、内存、I/O和网络资源使用情况&#xf…