【STL】 vector的底层实现

news2024/9/23 13:20:36

1.vector的模拟代码完整实现(后面会拆分开一个一个细讲)

#pragma once
#include<assert.h>

// 抓重点

namespace bit
{
	/*template<class T>
	class vector
	{
	public:
		typedef T* iterator;

	private:
		T* _a;
		size_t _size;
		size_t _capacity;
	};*/

	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;


		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end() const
		{
			return _finish;
		}

		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		// 类模板的成员函数
		// 函数模板 -- 目的支持任意容器的迭代器区间初始化
		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		vector(size_t n, const T& val = T()) //创建一个包含 n 个元素的向量,并且每个元素都被初始化为 val 的值。
			//如果没有提供 val 参数,则默认使用 T() 构造出的默认值。
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		vector(initializer_list<T> il)
		//vector(initializer_list<T> il):这是 vector 类的构造函数。它允许使用花括号 {} 括起来的一组元素来初始化 vector 对象。

		{
			reserve(il.size());
			for (auto e : il)
			{
				push_back(e);
			}
		}

		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

		// 强制编译器生成默认的
		vector() = default;

		// v2(v1)
		vector(const vector<T>& v)
		{
			reserve(v.capacity());
			for (auto e : v)
			{
				push_back(e);
			}
		}
		// 16:05继续
		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}

		// v1 = v3
		vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}

		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _end_of_storage = nullptr;
			}
		}

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t oldsize = size();
				T* tmp = new T[n];

				if (_start)
				{
					//memcpy(tmp, _start, sizeof(T) * old_size);
					for (size_t i = 0; i < oldsize; i++)
					{
						tmp[i] = _start[i];
					}

					delete[] _start;
				}

				_start = tmp;
				_finish = _start + oldsize;
				_end_of_storage = _start + n;
			}
		}

		size_t capacity() const
		{
			return _end_of_storage - _start;
		}

		size_t size()  const
		{
			return _finish - _start;
		}

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

			return _start[i];
		}

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

			return _start[i];
		}

		void push_back(const T& x)
		{
			/*if (_finish == _end_of_storage)
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);
			}

			*_finish = x;
			++_finish;*/

			insert(end(), x);
		}

		void pop_back()
		{
			assert(size() > 0);

			--_finish;
		}

		iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start);
			assert(pos <= _finish);

			if (_finish == _end_of_storage)
			{
				size_t len = pos - _start;

				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);

				pos = _start + len;
			}

			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}

			*pos = x;
			++_finish;

			return pos;
		}

		// 迭代器
		// 勿在浮沙筑高台
		void erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);

			iterator it = pos + 1;
			while (it != _finish)
			{
				*(it - 1) = *it;
				++it;
			}

			--_finish;
		}

	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};

2.vector的成员变量

在查看STL库里面vector的实现时,我们发现它是一个类模板并且定义了三个成员变量,分别是iterator start、iterator finish、 iterator end_of_storage用来标记开始,结束,以及总容量,对于vector来说其迭代器iterator就是T*,例如我们之前学习过的顺序表插入的是int类型的数据,所以对存放int类型的vector来说T*就是int*。以此类推,在定义的时候<>里面定义的类型就是容器中所存放的类型  char string list等都可以存放在里面

3.vector中的部分成员变量

template<class T>
class vector
{
public:
	typedef T* iterator;
	typedef const T* const_iterator;


	const_iterator begin() const
	{
		return _start;
	}

	const_iterator end() const
	{
		return _finish;
	}

	iterator begin()
	{
		return _start;
	}

	iterator end()
	{
		return _finish;
	}

4.vector中的构造函数

        1.默认构造

vector()
	:_start(nullptr)
	, _finish(nullptr)
	, _endOfStorage(nullptr)
{}
//上下等价
//vector() = default; //强制编译器生成默认的

        2.拷贝构造

// v2 (v1)
vector(const vector<T>& v)
{
	reserve(v.capacity());
	for (auto e : v) push_back(e);
}

        3.迭代器区间初始化

        

//类模板的成员函数,迭代器区间初始化
//函数模板  -- 目的支持任意容器迭代器区间初始化
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
	//reserve(last - first);
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

        4.n个val构造

//n个val构造
vector(size_t n, const T& val = T())//缺省值不能给0,因为T可能是string,所以给匿名对象T()
{
	reserve(n);
	while(n--)
	{
		push_back(val);
	}
}
 
//n个val构造,第一个参数为int类型
vector(int n, const T& val = T())//缺省值不能给0,因为T可能是string,所以给匿名对象
{
	reserve(n);
	while(n--)
	{
		push_back(val);
	}
}

5.对于构造函数的测试

void test_vector7()
{
	vector<string> v1(10);
	vector<string> v2(10, "xxx");
 
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
	for (auto e : v2)
	{
		cout << e << " ";
	}
	cout << endl;
 
	vector<int> v3(10,1); //若没有迭代器函数区间匹配,可以将就运行
	//vector <int> v3(10u, 1); //因为调用的是unsigned那个
	for (auto e : v3)
	{
		cout << e << " ";
	}
	cout << endl;
 
	vector<int> v4(10, 1);
	for (auto e : v4)
	{
		cout << e << " ";
	}
	cout << endl;
}

6.initializer_list构造

initializer_list是C++新增的一个类型,方便初始化,支持将花括号括起来的值initializer_list,initializer_list对象里面有两个指针,指向花括号里面值开始和结尾的下一个,并支持迭代器,所以可以使用范围for来遍历,当然这个要编译器支持将花括号传给容器

//initializer_list构造
vector(initializer_list<T> il)
{
    reserve(il.size());//size表示数据个数
    for (auto i : il)
    {
        push_back(i);
    }
}

6.1 initializer_list构造的测试代码

void test_vector8() //initializer_list构造测试
{
	//隐式类型转换
	vector<int> v1({ 1,2,3,4,5 });
	vector<int> v2 = { 6,7,8,9,10 };
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
	for (auto e : v2)
	{
		cout << e << " ";
	}
}

7.赋值运算符的重载

// v1 = v3
vector<T>& operator = (vector<T> v)
{
	swap(v);
	return *this;
}

这里使用传值传参,是实参的拷贝,所以我们将它与被赋值的对象交换后返回即可完成赋值,并且交换后形参生命周期结束就会自动调用析构函数释放原来的空间。

5.vector类中的容量操作

1.size 与 capacity

int main()
 {
 cout << "指定⼤⼩为10时:\n";
 vector<int> v(10, 1);
 cout << v.size() << endl;
 cout << v.capacity() << endl;
 vector<int> v1;
 v1.push_back(1);
 v1.push_back(1);
 v1.push_back(1);
 v1.push_back(1);
 cout << "插⼊四个数据时:\n";
 cout << v1.size() << endl;
 cout << v1.capacity() << endl;
 cout << "插⼊五个数据时:\n";
 v1.push_back(1);
 cout << v1.size() << endl;
 cout << v1.capacity() << endl;
 return 0;
 }
输出结果:
指定⼤⼩为10时:
10
 10
插⼊四个数据时:
4
 4
插⼊五个数据时:
5
 8

capacity的代码在g++ vs 和 clang下分别允许会发现,vs下capacity是按1.5倍增⻓,clang和g++是按2倍增⻓。这个 问题经常会考察,不要固化的认为,vector增容都是2倍,具体增⻓多少是根据具体的需求定义的。vs是PJ版本 STL,g++是SGI版本STL

2.resize

resize在开空间的同时还会进⾏初始化,影响size 使⽤resize()函数可以改变调⽤对象的size和capacity⼤⼩,如果指定的⼤⼩⼩于size时相当于删除数据,当指定⼤⼩ ⼤于size时则作⽤为扩容+初始化(对于int默认初始化为0)

int main()
 {
 vector<int> v(10, 1);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 cout << endl;
 //1 1 1 1 1 1 1 1 1 1 
v.resize(20);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 cout << endl;
 //1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 
v.resize(5);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 cout << endl;
 //1 1 1 1 1 
//指定⼤⼩⼩于当前的size时,再指定初始化内容时不会修改原始内容
v.resize(3, 3);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 //1 1 1
 return 0;
 }

3.reserve

reserve只负责开辟空间,如果确定知道需要⽤多少空间,reserve可以缓解vector增容的代价缺陷问题。 使⽤reserve()函数可以更改调⽤对象的capacity的⼤⼩,注意,如果指定⼤⼩⼩于当前的capacity时,则不会做任何 处理

int main()
 {
 vector<int> v(10, 1);
 cout << "原始⼤⼩:\n";
 cout << v.capacity() << endl;
 cout << v.size() << endl;
 cout << "扩容到20:\n";
 v.reserve(20);
 cout << v.capacity() << endl;
 cout << v.size() << endl;
 cout << "扩容到5:\n";
 v.reserve(5);
 cout << v.capacity() << endl;
 cout << v.size() << endl;
 return 0;
 }
输出结果:
原始⼤⼩:
10
 10
扩容到20:
20
 10
扩容到5:
20
 10

6.vector类中的数据遍历操作

void test2(){
 vector<int> v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
//迭代器遍历
//指明迭代器类型
vector<int>::iterator it1 = v.begin();
 while (it1 != v.end()) {
 cout << *it1 <<" ";
 it1++;
  }
 cout << endl;//1 2 3 4
 //下标遍历
for (size_t i = 0; i < v.size(); i++) {
 cout << v[i] << " ";
  }
 cout << endl;//1 2 3 4
 //范围for
 for(auto &e : v){
 cout << e <<" ";
  }
 cout << endl;//1 2 3 4
 }
 int main(){
 test2();
 }

6.2 operator 与at()函数

6.3 正向遍历begin()和end()迭代器——⾮const

#include <iostream>
 #include <vector>
 using namespace std;
 int main()
 {
 vector<int> v(10, 1);
 //正向迭代器——⾮const
 vector<int>::iterator it = v.begin();
 while (it != v.end())
  {
 *it = 2;//可修改
cout << *it << " ";
 it++;
  }
 return 0;
 }
输出结果:
2 2 2 2 2 2 2 2 2 2

6.4逆向遍历rbegin()和rend()迭代器——⾮const

#include <iostream>
 #include <vector>
 using namespace std;
 int main()
 {
 vector<int> v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
 //逆向迭代器——⾮const
 vector<int>::reverse_iterator rit = v.rbegin();
 while (rit != v.rend())
  {
  (*rit)++;//可修改
cout << *rit << " ";
rit++;
  }
 return 0;
 }
输出结果:
5 4 3 2

6.5 assign()函数

//使⽤⼀个对象的迭代器区间为调⽤对象分配内容
#include <iostream>
 #include <vector>
 using namespace std;
 int main()
 {
 //vector<int> v(5, 1);
 vector<int> v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
 for (auto num : v)
  {
 cout << num << " ";
  }
cout << endl;
 vector<int> v1(10, 2);
 //v.assign(v1.begin(), v1.end());
 v.assign(v1.begin(), v1.end());
 for (auto num : v)
  {
 cout << num << " ";
  }
 return 0;
 }
输出结果:
1 2 3 4
 2 2 2 2 2 2 2 2 2 2
 //指定n个内容为调⽤对象分配
#include <iostream>
 #include <vector>
 using namespace std;
 int main()
 {
 //vector<int> v(5, 1);
 vector<int> v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
 for (auto num : v)
  {
 cout << num << " ";
  }
 cout << endl;
 //vector<int> v1;
 //v1.push_back(5);
 //v1.push_back(6);
 //v1.push_back(7);
 //v1.push_back(8);
 //v.assign(v1.begin(), v1.end());
 v.assign(10, 5);
 for (auto num : v)
  {
 cout << num << " ";
  }
 return 0;
 }
输出结果:
1 2 3 4
 5 5 5 5 5 5 5 5 5 5

7.插入insert

iterator insert(iterator pos, const T& x)
{
	assert(pos >= _start);
	assert(pos <= _finish);
 
	if (_finish == _end_of_storage)
	{
		//防止迭代器因为后面的扩容失效,所以要提前记录pos位置
		size_t len = pos - _start;
		//size_t len = size();
 
		size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
		reserve(newcapacity);
 
		pos = _start + len; //判断扩容之后os位置,防止野指针出现使迭代器失效
	}
 
	iterator end = _finish - 1; //后移
	while (end >= pos)
	{
		*(end + 1) = *end;
		--end;
	}
 
	*pos = x;
	++_finish;
	return pos;
}

这里要注意迭代器因为扩容导致pos失效的问题(野指针),所以要提前规避,记录好pos相对位置,然后再即时更新pos迭代器,否则就会出现随机值;

此外,insert的参数pos是对实参的拷贝,形参的改变不会影响实参,所以外部的实参也会失效,但是我们也不能通过引用传参,因为其迭代器返回的是临时拷贝具有常性不能通过引用传参,所以这里我们就可以通过控制insert函数的返回值来解决,我们会返回更新后的迭代器,这样就可以访问该位置了。

7.2push_back可以直接复用insert进行实现

//尾插
void push_back(const T& x)
{
	insert(end(), x);
}

7.3push_back与pop_back函数的测试

//在指定的的迭代器位置后插⼊内容val
 #include <iostream>
 #include <vector>
 using namespace std;
int main()
 {
 vector<int> v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 cout << endl;
 v.insert(v.begin() + 3, 5);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 return 0;
 }
输出结果:
1 2 3 4
 1 2 3 5 4

7.4在指定的迭代器位置后开始插⼊n个val

//在指定的迭代器位置后开始插⼊n个val
 #include <iostream>
 #include <vector>
 using namespace std;
 int main()
 {
 vector<int> v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 cout << endl;
 v.insert(v.begin() + 3, 5, 10);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 return 0;
 }
输出结果:
1 2 3 4
 1 2 3 10 10 10 10 10 4

7.5在指定的迭代器位置后开始插⼊⼀个其他对象对应的迭代器区间中的内容

#include <iostream>
 #include <vector>
 using namespace std;
 int main()
 {
 vector<int> v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 cout << endl;
 vector<double> v1;
 v1.push_back(5.2);
 v1.push_back(6.3);
 v1.push_back(7.4);
 v1.push_back(8.5);
 //根据调⽤对象的类型决定强制转换的类型
v.insert(v.begin() + 3, v1.begin(), v1.end());//将double强制转换成int类型存⼊int类型的vector对象
for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 cout << endl;
 v1.insert(v1.begin() + 3, v.begin(), v.end());//将int强制转换成double类型存⼊double类型的vector
对象
}
 for (size_t i = 0; i < v1.size(); i++)
  {
 cout << v1[i] << " ";
  }
 return 0;
输出结果:
1 2 3 4
 1 2 3 5 6 7 8 4
 5.2 6.3 7.4 1 2 3 5 6 7 8 4 8.5

8.删除erase

iterator erase(iterator pos)
{
	assert(pos >= _start);
	assert(pos < _finish);
 
	iterator it = pos + 1;
	while (it != _finish) //从pos + 1开始往前移动
	{
		*(it - 1) = *it;
		++it;
	}
 
	--_finish;
	return pos;
}

erase()之后迭代器失效问题

  • 有可能删除之后缩容
  • 删除最后一个位置会导致越界访问

所以我们认为删除操作之后迭代器也会失效,和插入函数一样通过返回迭代器来更新迭代器使用才行

8.2 删除指定位置的⼀个数据

//删除指定位置的⼀个数据
#include <iostream>
 #include <vector>
 using namespace std;
 int main()
 {
 vector<int> v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 cout << endl;//1 2 3 4
 vector<int>::iterator it = v.begin() + 1;
 v.erase(it);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 cout << endl;//1 3 4
 //不可以再删除迭代器⼀开始指向的位置,存在迭代器失效问题
//v.erase(it);
 //如果还需要删除下标为1的位置需要再将迭代器更新
it = v.begin() + 1;
 v.erase(it);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";//1 4
  }
 return 0;
 }

8.3 删除迭代器区间中的内容

 //删除迭代器区间中的内容
#include <iostream>
 #include <vector>
using namespace std;
 int main()
 {
 vector<int> v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 cout << endl;//1 2 3 4
 v.erase(v.begin() + 2, v.end());
 for (size_t i = 0; i < v.size(); i++)
  {
 cout << v[i] << " ";
  }
 cout << endl;//1 2
 return 0;
 }

9.swap函数

使⽤swap()函数可以交换调⽤对象和指定对象中的数据

#include <iostream>
 #include <vector>
 using namespace std;
 int main()
 {
 vector<int> v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
 vector<int>    
v1(5, 1);
 cout << "交换前:\n";
 cout << "v的size:" << v.size() << endl;//4
 cout << "v的capacity:" << v.capacity() << endl;//4
 for (auto num : v)
  {
 cout << num << " ";
  }
 cout << endl;//1 2 3 4
 cout << "v1的size:" << v1.size() << endl;//5
 cout << "v1的capacity:" << v1.capacity() << endl;//5
for (auto num : v1)
  {
 cout << num << " ";
  }
 //1 1 1 1 1
 v.swap(v1);
 cout << "\n交换后:\n";
 cout << "v的size:" << v.size() << endl;//5
 cout << "v的capacity:" << v.capacity() << endl;
 for (auto num : v)
  {
 cout << num << " ";
  }//11111
 cout << endl;
 cout << "v1的size:" << v1.size() << endl;//4
 cout << "v1的capacity:" << v1.capacity() << endl;//4
 for (auto num : v1)
  {
 cout << num << " ";
  }//1234
 return 0;
 }

10.find函数

#include <iostream>
 #include <vector>
 #include <algorithm>
 using namespace std;
 int main()
 {
 vector<int> v;
 v.push_back(1);
 v.push_back(2);
 v.push_back(3);
 v.push_back(4);
 vector<int>::iterator it = find(v.begin(), v.end(), 3);
cout << *it << endl;//3
    return 0;
 }

11.某帅哥做的笔记——pdf

vector

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

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

相关文章

avl树自实现(带图),探讨平衡因子与旋转

引子&#xff1a; 在此之前&#xff0c;我们学过了搜索二叉树&#xff0c;这种树&#xff0c;在如果数据有序或接近有序的情况下&#xff0c;二叉搜索树将退化为单支树&#xff0c;查找元素相当于在顺序表中搜索元素&#xff0c;效率低下&#xff0c;而且普通搜索二叉树无法有…

计算机“八股文”在实际工作中是助力、阻力还是空谈?

“八股文”在实际工作中是助力、阻力还是空谈&#xff1f; 作为现在各类大中小企业面试程序员时的必问内容&#xff0c;“八股文”似乎是很重要的存在。但“八股文”是否能在实际工作中发挥它“敲门砖”应有的作用呢&#xff1f;有IT人士不禁发出疑问&#xff1a;程序员面试考…

ReentrantLock的阻塞性、可中断性

结论&#xff1a; lock() 如果没有获取到锁&#xff0c;会一直阻塞并尝试获取锁&#xff0c;直到获取到锁。lock() 获取到锁之前&#xff0c;其他线程不可以中断该线程。因为线程Thread如线程t2的interrupt方法&#xff0c;想要中断线程&#xff0c;但不会真的中断&#xff0c…

如何把uniapp 项目发布成Andriod App的流程

1&#xff1a;点击HBuilderX 状态栏左侧的未登录按钮&#xff0c;弹出登录的对话框。 2: 在弹出的登录的对话框中, 填写账号和密码之后, 点击登录即可。 3&#xff1a; 打开项目根目录中的 mainfest.json 配置文件&#xff0c; 在基础配置的面板中&#xff0c; 获取uniapp 的应…

Redis2-Redis常见命令

目录 Redis数据结构介绍 Redis通用命令 KEYS DEL EXISTS EXPIRE String类型 Key的层级格式 Hash类型 List类型 Set类型 SortedSet类型 Redis数据结构介绍 Redis是一个key-value的数据库&#xff0c;key一般是String数据库&#xff0c;value的类型多种多样 可以通过…

SQL回顾

一、知识点回顾 1、数据库的分类。 ①关系型数据库。&#xff08;磁盘、持久化&#xff09; 例&#xff1a;MySQL&#xff08;搭配php、java&#xff09;。 ②非关系型数据库。&#xff08;暂存、存在内存中&#xff09; 例&#xff1a;sql server&#xff08;搭配.net&…

四、图片生成视频

具体步骤 1、安装插件(VideoHelperSuite) 2、创建工作流 2.1 双击搜索svd 2.2 添加节点 2.3 选择clip视觉长按拖住生成checkpoint加载器 2.4 选择vae对应连接 2.4 选择图像添加加载图像节点 2.4 选择正面条件 选择k采样器节点新增 2.5 进行节点间的连接&#xff08;模型 …

基于开源FFmpeg和SDL2.0的音视频解码播放和存储系统的实现

目录 1、FFMPEG简介 2、SDL简介 3、视频播放器原理 4、FFMPEG多媒体编解码库 4.1、FFMPEG库 4.2、数据类型 4.3、解码 4.3.1、接口函数 4.3.2、解码流程 4.4、存储&#xff08;推送&#xff09; 4.4.1、接口函数 4.4.2、存储流程 5、SDL库介绍 5.1、数据结构 5.…

西部菱斑响尾蛇教你基础IO

快学&#xff0c;再不学普洱就要超过你们了 在C阶段进行的文件操作有哪些呢&#xff1f; #include<stdio.h> #include<string.h>int main() {FILE* fp fopen("myfile", "w");if (!fp){printf("fopen error!\n");}const char* msg …

大模型系列:大模型tokenizer分词编码算法BPE理论简述和实践

关键词&#xff1a;大语言模型&#xff0c;分词&#xff0c;BPE&#xff0c;BBPE 前言 token是大模型处理和生成语言文本的基本单位&#xff0c;在之前介绍的Bert和GPT-2中&#xff0c;都是简单地将中文文本切分为单个汉字字符作为token&#xff0c;而目前LLaMA&#xff0c;Cha…

云原生高级必备基础

一.文件管理 相对路径和绝对路径 touch 创建文件 mkdir 创建目录 -p多级创建 rm 删除 -i 删除前逐一询问确认。 -f 即使原档案属性设为唯读&#xff0c;亦直接删除&#xff0c;无需逐一确认。 -r 将目录及以下之档案亦逐一删除。 cp 复制 -p -r mv 移动 cp和mv的区别 …

8月4号分析:CSGO市场行情如何,给几个操作建议

很多粉丝让我聊聊对近期CSGO饰品市场的看法&#xff0c;那今天就简单聊聊&#xff01; 最近的CSGO市场&#xff0c;从在线人数就可以看出来&#xff0c;这段时间是实打实的流失了很多玩家&#xff0c;就目前这个情况&#xff0c;120万的在线人数里面&#xff0c;至少还有10多万…

sql注入之无列名注入

目录 一、基础知识 二、平替information_schema库 三、无列名注入 3.1 正常无列名查询&#xff1a; 3.2 子查询&#xff1a; 3.3 实战 一、基础知识 我们在注入的过程中很多时候利用的就是information_schema这个库获取 table_schema table_name, column_name这些数据库内…

一键转换语言,五款强大文件翻译软件推荐!

在当今的职场环境中&#xff0c;跨语言沟通已成为常态。无论是与国际客户洽谈业务&#xff0c;还是处理海外项目报告&#xff0c;精准高效的文件翻译能力都是每位职场人士的必备技能。今天&#xff0c;我们就来盘点几款职场人士必备的文件翻译工具。 福昕在线翻译&#xff1a;…

PXE批量安装——————rhel7

实验前准备 什么是PXE&#xff1f; PXE是一种基于网络的启动技术&#xff0c;它集成了在计算机的BIOS或UEFI中&#xff0c;允许计算机从网络服务器下载并启动操作系统或其他软件。 应用场景 无盘工作站&#xff1a;在教育和科研机构中&#xff0c;无盘工作站通过PXE启动操作…

字符串切割split

let obj {} let str "aa占比:17.48%,aa计费占比:0.00%" let arr str.split(,) // [aa占比:17.48%,aa计费占比:0.00%] arr.forEach(item > { let [key,value] item.split(:) obj[key] value }) console.log(obj) //{aa占比: 17.48%, aa计费占比: 0.00%} con…

Markdown文本编辑器:Typora for Mac/win 中文版

Markdown 是一种轻量级的标记语言&#xff0c;它允许用户使用易读易写的纯文本格式编写文档。Typora 支持且仅支持 Markdown 语法的文本编辑&#xff0c;生成的文档后缀名为 .md。 这款软件的特点包括&#xff1a; 实时预览&#xff1a;Typora 的一个显著特点是实时预览&#x…

lombok安装成功但是找不到方法

2024.1.1版本的IDE的插件安装了默认的lombok&#xff08;如图1&#xff09;&#xff0c;pom文件中也引入了lombok的依赖&#xff0c;在实体类写了Data的注解&#xff0c;当调用实体类的get和set方法运行时&#xff0c;报错找不到相应的方法&#xff0c;但是在调用get、set方法的…

Java实现全局异常统一处理

Java实现全局异常统一处理 【一】介绍【二】为什么要使用全局异常处理【三】案例实现【1】GlobalException【2】GlobalExceptionHandler【3】使用 【一】介绍 全局异常处理器是一种在应用程序中几种处理异常的机制&#xff0c;它允许在应用程序的任何地方抛出异常时&#xff0…

2024年6月scratch图形化编程等级考试一级真题

202406 scratch编程等级考试一级真题 选择题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 1、音乐Video Game1的时长将近8秒&#xff0c;点击一次角色&#xff0c;下列哪个程序不能完整地播放音乐两次 A、 B、 C、 D、 答案&#xff1a;D 考点分…