vector类的模拟实现

news2024/10/7 2:19:37

 实现基本的vector框架

参考的是STL的一些源码,实现的vector也是看起来像是一个简略版的,但是看完能对vector这个类一些接口函数更好的认识。

我们写写成员变量,先来看看STL的成元变量是那些

namespace tjl
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		vector()
			:_start(nullptr)
			,_finish(nullptr)
			,_eof(nullptr)
		{}

	private:
		iterator _start;//
		iterator _finish;
		iterator _eof;//表示的是end_of_storage
	};

}

这里的成员变量有些不一样,我们也用iterator来表示,其实这里就是一个指针,但是用迭代器的名称来称呼它,还有我们这里加上了模板,更加凸显了vector的不一样,这样的好处就是我们这个vector这个类就可以去适应各种不同的类型,达到一个____效果(填一个四字成语)。

实现构造函数

构造函数的意义就是来进行初始化,因为我们构造函数里面都是会走初始化列表的这个过程的,所以我们就可以写成这个样子。

vector()
			:_start(nullptr)
			,_finish(nullptr)
			,_eof(nullptr)
		{}

但是要注意的是我们的库里面的构造函数可不是只有这些,大家可以也来查询我们的网站来查看

vector构造函数

我们可以看到他是可以支持迭代器进行构造的,也就是说我们这里可以使用任意类型来进行构造

也可以用给值的方式,所以vector的作用还是很大的。
 

析构函数的实现

析构函数的作用就是在我们程序结束的时候对我们的空间进行释放,所以我们可以这样写

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

这里我们可以增加if这个判断,因为_start可能是为空的,所以我们可以写一个对它进行检查的功能。

实现vector的size()和capacity()

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

我们这里的实现细节其实只要注意的是const,因为我们可能是const对象进行调用,所以这里可以加上const,防止我们的权限进行放大。

实现push_back

这里的实现会有点小细节值的我们注意,我们先来想想push_back是在尾部插入数据·就可以了,但是我们可不可能如果当我们的空间是满的时候,就会遇到需要扩容的问题,所以这个时候我们需要先来写一个reserve的函数来进行扩容

reserve函数的实现

因为我们每次扩容的时候到要用到这个函数,比如尾插还是随机插入都会用到,那我们的思路是那些还有注意事项呢?

首先我们得先知道一个问题就是我们当什么时候才是要扩容的时候,因为reserve会传一个参数n

表示我们得开多大的空间,所以我们需要做的时候就是先判断要不要开这么大的空间。

先来看我们的代码

void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				size_t pos = _finish - _start;
				if (_start)
				{
					memcpy(tmp, _start, sizeof(T) * size());
					delete[]_start;
				}
				_start = tmp;
				_finish = _start + pos;
				_eof = _start + n;

			}
		}

思路就是我们重新开辟一块空间,然后把原来空间的内容拷贝到新内容上,这里要注意的是当我们赋值的时候,就是给_start和_finish给值的时候要先记录pos位置,因为我们扩容的时候是重新开辟的,可能存在_finish进行赋值的时候它还是指向空,这样就出现空指针的现象了。

那我们实现reserve之后就可以继续来实现push_back函数了。

void push_back(const T& val)
		{
			if (_finish == _eof)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);

			}
			*_finish = val;
			++_finish;
		}

先检查空间是不是够,如果空间不够我们就需要进行扩容,其次就是大家这里要明白一个点,为什么我们可以对_finish进行解引用,然后直接进行给值,原因就是val的类型。

实现operator[]

这个很简单直接一把过了。

	const T& operator[](size_t pos) const
		{
			return _start[pos];
		}

用[]进行遍历来看看。

void test1()
{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		for (int i = 0; i < v.size(); i++)
		{
			cout << v[i] << " ";

		}
}

迭代器的实现

iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}

因为迭代器我们必须要给它命名begin和end要不然就不能使用范围for来进行遍历,我们现在可以用迭代器来对我们的数据进行遍历,也能使用范围for,这里两种方式都写出来给大家看看。

void test1()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		for (int i = 0; i < v.size(); i++)
		{
			cout << v[i] << " ";

		}
		cout << endl;
		for (auto e : v)
		{
			cout << e << " ";
		}
		auto it = v.begin();
		while (it != v.end())
		{
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}

这样有三种方式可以对我们进行遍历了,完成迭代器只后需要来处理一些细节问题,还是我们权限放大的问题,因为当我们用const的对象去调用的时候就不行了,这里只需要在写一个const版本的🆗了,我们可以先typedef一下。

typedef const T* const_iterator;

这样就表示这个迭代器是const修饰过的迭代器。那它的begin和end就是这样写的。

const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}

不过我们对const的迭代器只能进行读的操作就不能进行写操作了。

迭代器失效问题

这个问题是出现在insert和erase的时候是会出现问题

首先我们实现insert需要注意的细节有哪些

和我们之前遇到的顺序表其实本质是没有多大区别的,这里用的是指针,而不是数组下标,因为这里模拟实现的时候,其实我们的迭代器就是原生指针,虽然和VS里的不一样,Vs里的iterator并不是原生指针,所以很好实现我们这里的insert,来看看代码吧。

iterator insert(iterator pos, const T& val = T())
		{
			assert(pos >= begin());
			assert(pos <= end());
			size_t len = pos - _start;
			if (_finish == _eof)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				end--;
			}
			*pos = val;
			++_finish;
			return pos;
		}

其实这里我就一步到位了,但是还是有很多的细节值的我们去注意,第一个就是为什么说这里会有迭代器失效的问题,首先就是如果我们步扩容的时候迭代器是不会失效的,但是扩容之后的pos还是指向原来控空间的,因为我们这里的扩容的步骤是开空间,然后对我们的内容进行值拷贝,释放之前的空间,这样pos就是一个野指针,所以我们需要做的就是更新pos位置到新的空间上,可以先记住pos的相对位置,然后更新。所以这里这个坑是没有位置的,还有一个需要注意的是我们需要更新外面的pos,因为形参的改变是不会影响实参的改变的,所以这里的一个重要的步骤就是返回pos的值。

迭代器的失效:第一个重要的原因就是这个指针变成野指针了,我们需要更新它

第二个原因就是当我们进行insert的时候需要进行返回,因为我们在这个函数内改变了,但是在外面还是没有进行改变(形参的改变是不会影响实参的改变的)

我们来进行测试一下

void test2()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
		auto pos = find(v.begin(), v.end(), 2);
		v.insert(pos, 100);
		for (auto e : v)
		{
			cout << e << " ";
		}
	}

发现最后的结果也是正确的,这样还要提醒大家,我们认为insert和erase之后的迭代器是失效的,不再使用,虽然我们可以接受pos位置,但是最好还是不要使用。

实现erase

iterator erase(iterator pos)
		{
			assert(pos >= begin());
			assert(pos < end());
			iterator start = pos;
			while (start < _finish)
			{
				*start = *(start + 1);
				start++;
			}
			_finish--;
			return pos;
		}

这个erase也是很简单,就是移动进行数据的覆盖就可以解决问题了。我们也来测试一下看看结果是不是对的。

void test3()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
		auto pos = find(v.begin(), v.end(), 2);
		v.erase(pos);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

深浅拷贝问题

先来实现拷贝构造。

拷贝构造的实现可以有现代写法和原始写法,我们先来写原始写法那个,然后引出我们的深浅拷贝的问题。

	vector(const vector<T>& v)
		{
			reserve(v.capacity());
			memcpy(_start, v._start, sizeof(T) * v.size());

		}

我们这里进行的值拷贝,是按照字节的拷贝的,如果我们T是内置类型的时候,我们的的代码是不会出现问题的,但是如果是string或者里面还是一个vector<int>的时候代码就会出现问题,我们可以先来看看string的结果。

void test4()
	{
		vector<string> v;
		v.push_back("111111");
		v.push_back("111111");
		v.push_back("111111");
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
		vector<string> v1(v);
		for (auto e : v1)
		{
			cout << e << " ";
		}

	}

我们这样写的时候就是会出现错误,原因是我们这里的拷贝构造是进行的值拷贝,不过如果我们不写这个拷贝构造的时候是不会出现问题的,原因string会去调用它自己的拷贝构造。

但是其根本原因还是我们扩容的是进行的是值拷贝和我们没有写拷贝构造造成的问题。现在来修改一下reserve。

void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				size_t pos = _finish - _start;
				if (_start)
				{
					//memcpy(tmp, _start, sizeof(T) * size());
					for (size_t i = 0; i < size(); i++)
					{
						tmp[i] = _start[i];
					}
					delete[]_start;
				}
				_start = tmp;
				_finish = _start + pos;
				_eof = _start + n;

			}
		}

然后加上我们的考拷贝构造。

vector(const vector<T>& v)
		{
			reserve(v.capacity());
			for (size_t i = 0; i < v.size(); i++)
			{
				_start[i] = v._start[i];
			}
			_finish = _start + v.size();
			_eof = _start + v.capacity();
		}

这个就是原始的写法,这样我们的代码是没有问题的,但是这里为什么没有问题的第一个原因就是string是我们库里面的,进行赋值的时候会去调用它的赋值重载,但是如果我们这里的类型是vector<vector<int>>又会出现问题,我们可以来看看,如果我们需要拷贝一个杨辉三角的时候,会不会有问题。

下面来演示一下,我们可以先把杨辉三角的代码直接先拿过来

class Solution {
	public:
		vector<vector<int>> generate(int numRows) {
			vector<vector<int>> vv;
			vv.resize(numRows, vector<int>());//进行初始化
			//进行的是每行初始化,因为这里表示的是顺序表里面是个顺序表
			for (int i = 0; i < vv.size(); i++)//初始化没列
			{
				vv[i].resize(i + 1, 0);
				vv[i][0] = vv[i][vv[i].size() - 1] = 1;
			}
			for (int i = 0; i < vv.size(); i++)
			{
				for (int j = 0; j < vv[i].size(); j++)
				{
					if (vv[i][j] == 0)
					{
						vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j];
					}
				}
			}
			return vv;
		}
	};

然后进行拷贝一个杨辉三角,看看有没有什么问题

void test5()
	{

		vector<vector<int>> v = Solution().generate(5);
		auto v1(v);
		for (int i = 0; i < v1.size(); i++)
		{
			for (int j = 0; j < v1[i].size(); j++)
			{
				cout << v1[i][j] << " ";
			}
			cout << endl;
		}
	}

熟悉的感觉,真是太美妙了 

那我们其实可以通过调试来看看问题所在的地方。

 

可以看到什么不一样的地方,一个就是我们的外面的大vector是完成了深拷贝,里面还是没有,为什么其他类型的string就可以,因为string有它自己的赋值重载,我们这里没有写vector的赋值重载,所以才会有这样的问题,那我们只需要写一个赋值重载就可以解决问题了。

 

void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_eof, v._eof);
		}
		vector<T>& operator=( vector<T>& v)
		{
			swap(v);
			return *this;
		}

这样我们的问题就能够很好的解决了。

那我们再来完善一下其他的接口函数就解决了。

pop_back的实现
void pop_back()
		{
			erase(--end());
		}
	template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

完整的代码实现

#pragma once
#include<assert.h>
namespace tjl
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		vector()
			:_start(nullptr)
			,_finish(nullptr)
			,_eof(nullptr)
		{}
		vector(size_t n, const T& val = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _eof(nullptr)
		{
			reserve(n);
			while (n--)
			{
				push_back(val);
			}

		}
		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_eof, v._eof);
		}
		vector<T>& operator=( vector<T> v)
		{
			swap(v);
			return *this;
		}
		vector(int n, const T& val = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _eof(nullptr)
		{
			reserve(n);

			while (n--)
			{
				push_back(val);
			}

		}
		template<class inputiterator>
		vector(inputiterator first, inputiterator last)
			: _start(nullptr)
			, _finish(nullptr)
			, _eof(nullptr)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		vector(const vector<T>& v)
			: _start(nullptr)
			, _finish(nullptr)
			, _eof(nullptr)
		{
			reserve(v.capacity());
			for (size_t i = 0; i < v.size(); i++)
			{
				_start[i] = v._start[i];
			}
			_finish = _start + v.size();
			_eof = _start + v.capacity();
		}

		void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				size_t pos = _finish - _start;
				if (_start)
				{
					//memcpy(tmp, _start, sizeof(T) * size());
					for (size_t i = 0; i < size(); i++)
					{
						tmp[i] = _start[i];
					}
					delete[]_start;
				}
				_start = tmp;
				_finish = _start + pos;
				_eof = _start + n;

			}
		}
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const T& operator[](size_t pos) const
		{
			return _start[pos];
		}
		T& operator[](size_t pos) 
		{
			return _start[pos];
		}
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}
		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else 
			{
				reserve(n);
				while (_finish != _start + n)
				{
					*_finish = val;
					_finish++;
				}
			}
		}
		void push_back(const T& val)
		{
			if (_finish == _eof)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);

			}
			*_finish = val;
			++_finish;
		}
		size_t size()const
		{
			return _finish - _start;
		}
		size_t capacity() const
		{
			return _eof - _start;
		}
		iterator insert(iterator pos, const T& val = T())
		{
			assert(pos >= begin());
			assert(pos <= end());
			size_t len = pos - _start;
			if (_finish == _eof)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				end--;
			}
			*pos = val;
			++_finish;
			return pos;
		}
		iterator erase(iterator pos)
		{
			assert(pos >= begin());
			assert(pos < end());
			iterator start = pos;
			while (start < _finish)
			{
				*start = *(start + 1);
				start++;
			}
			_finish--;
			return pos;
		}

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

	
	private:
		iterator _start;//
		iterator _finish;
		iterator _eof;//表示的是end_of_storage
	};

	class Solution {
	public:
		vector<vector<int>> generate(int numRows) {
			vector<vector<int>> vv;
			vv.resize(numRows, vector<int>());//进行初始化
			//进行的是每行初始化,因为这里表示的是顺序表里面是个顺序表
			for (int i = 0; i < vv.size(); i++)//初始化没列
			{
				vv[i].resize(i + 1, 0);
				vv[i][0] = vv[i][vv[i].size() - 1] = 1;
			}
			for (int i = 0; i < vv.size(); i++)
			{
				for (int j = 0; j < vv[i].size(); j++)
				{
					if (vv[i][j] == 0)
					{
						vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j];
					}
				}
			}
			return vv;
		}
	};
	void test1()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		for (int i = 0; i < v.size(); i++)
		{
			cout << v[i] << " ";

		}
		cout << endl;
		for (auto e : v)
		{
			cout << e << " ";
		}
		auto it = v.begin();
		while (it != v.end())
		{
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}

	void test2()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
		auto pos = find(v.begin(), v.end(), 2);
		v.insert(pos, 100);
		for (auto e : v)
		{
			cout << e << " ";
		}
	}

	void test3()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
		auto pos = find(v.begin(), v.end(), 2);
		v.erase(pos);
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test4()
	{
		vector<string> v;
		v.push_back("111111");
		v.push_back("111111");
		v.push_back("111111");
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
		vector<string> v1(v);
		for (auto e : v1)
		{
			cout << e << " ";
		}

	}

	void test5()
	{

		vector<vector<int>> v = Solution().generate(5);
		auto v1(v);
		for (int i = 0; i < v1.size(); i++)
		{
			for (int j = 0; j < v1[i].size(); j++)
			{
				cout << v1[i][j] << " ";
			}
			cout << endl;
		}
	}

}

今天的分享就到这里了,我们下次再见!!!!

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

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

相关文章

极限的反问题【高数笔记】

1. 什么是极限反问题&#xff1f; 2. 极限反问题分为几类&#xff1f; 3. 每一类极限反问题的具体做法是什么&#xff1f; 4. 每一类极限反问题具体做法是否有前提条件&#xff1f; 5. 例题&#xff1f;

网络分析仪的防护技巧

VNA的一些使用防护技巧&#xff0c;虽不全面&#xff0c;但非常实用&#xff1a; [1] 一定要使用正规接地的三相交流电源线缆进行供电&#xff0c;地线不可悬浮&#xff0c;并且&#xff0c;火线和零线不可反接&#xff1b; [2] 交流供电必须稳定&#xff0c;如220V供电&#x…

windowsserver 2016 PostgreSQL9.6.3-2升级解决其安全漏洞问题

PostgreSQL 身份验证绕过漏洞(CVE-2017-7546) PostgreSQL 输入验证错误漏洞(CVE-2019-10211) PostgreSQL adminpack扩展安全漏洞(CVE-2018-1115) PostgreSQL 输入验证错误漏洞(CVE-2021-32027) PostgreSQL SQL注入漏洞(CVE-2019-10208) PostgreSQL 安全漏洞(CVE-2018-1058) …

ISIS 特性验证(ATT置位、渗透、认证)

拓扑图 配置 sysname AR1 # isis 1is-level level-1cost-style widenetwork-entity 49.0001.0000.0000.0001.00 # interface GigabitEthernet0/0/0ip address 12.1.1.1 255.255.255.0 isis enable 1 # interface GigabitEthernet0/0/1ip address 13.1.1.1 255.255.255.0 isis e…

李宏毅LLM——大模型+大资料的神奇力量

文章目录 大模型的重要性顿悟时刻 大资料的重要性数据预处理不一样的做法&#xff1a;KNN LM 对应视频P12-P14 大模型的重要性 模型参数和数据集越大&#xff0c;文字接龙的错误率越低 顿悟时刻 当模型超过10B-20B时&#xff0c;会突然顿悟 启示&#xff1a;不能只看最终结…

股票K线简介

股票K线&#xff08;K-Line&#xff09;是用于表示股票价格走势的图形&#xff0c;主要由四个关键价格点组成&#xff1a;开盘价、收盘价、最高价和最低价。K线图广泛应用于股票市场技术分析中&#xff0c;它提供了丰富的信息&#xff0c;帮助分析师和投资者理解市场的行情走势…

tee漏洞学习-翻译-2:探索 Qualcomm TrustZone的实现

原文&#xff1a;http://bits-please.blogspot.com/2015/08/exploring-qualcomms-trustzone.html 获取 TrustZone image 从两个不同的位置提取image 从手机设备本身从google factory image 已经root的Nexus 5设备&#xff0c;image存储在eMMC芯片上&#xff0c;并且eMMC芯片…

爬虫工作量由小到大的思维转变---<第四十六章 Scrapyd 用gerapy管理多台机器爬虫(2)>

前言: 继续上一篇爬虫工作量由小到大的思维转变---&#xff1c;第四十四章 Scrapyd 用gerapy管理多台机器爬虫&#xff1e;-CSDN博客 要想在电脑B上,部署爬虫应该做哪些? 正文: 前期准备: 1.已经成功在电脑A上启动了gerapy.并能够成功连接电脑A的ip; 原理: 首先,我需要…

图数据库 之 Neo4j - 环境搭建(2)

运行环境&#xff1a; centos7 Docker version 18.09.6 下载镜像 docker search neo4j docker pull neo4j 创建 neo4j 用户 # 创建 neo4j 用户 # -M 不创建用户的主目录 sudo useradd -M neo4j # usermod 用于修改用户属性命令 # -L 锁定用户&#xff0c;用户无法登录系统 user…

C++中的闭包

在编程语言中&#xff0c;闭包(closure)&#xff0c;又称为词法闭包(lexical closure)或函数闭包(function closure)&#xff0c;是一种在具有一流函数的语言中(a language with first-class functions)实现词法作用域名称绑定的技术。从操作上来说&#xff0c;闭包是一个将函数…

HiveSQL——借助聚合函数与case when行转列

一、条件函数 if 条件函数 if函数是最常用到的条件函数&#xff0c;其写法是if(xn,a,b), xn代表判断条件&#xff0c;如果xn时&#xff0c;那么结果返回a ,否则返回b。 selectif(age < 25 or age is null, 25岁以下, 25岁以上) as age_cnt,count(1) as number from table…

C语言第二十弹---指针(四)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 指针 1、字符指针变量 2、数组指针变量 2.1、数组指针变量是什么&#xff1f; 2.2、数组指针变量怎么初始化 3、⼆维数组传参的本质 4、函数指针变量 4.1…

spring boot(2.4.x之前版本)和spring cloud项目中自动装配的监听执行顺序

目录 扫描 org.springframework.context.ApplicationListener 指定的类 内置的监听 spring boot 中的监听 spring boot autoconfigure 中的监听 spring boot context 中的监听 将加载的监听进行排序 spring boot 中的监听 spring boot context 中的监听 监听执行 监听…

Apache Paimon 文件操作

本文旨在澄清不同文件操作对文件的影响。 本页面提供具体示例和实用技巧&#xff0c;以有效地管理这些操作。此外&#xff0c;通过对提交&#xff08;commit&#xff09;和压实&#xff08;compact&#xff09;等操作的深入探讨&#xff0c;我们旨在提供有关文件创建和更新的见…

006集——where语句进行属性筛选——arcgis

在arcgis中&#xff0c; dBASE 文件除了 WHERE 语句以外&#xff0c;不支持 其它 SQL 命令。选择窗口如下&#xff1a; 首先&#xff0c;我们了解下什么是where语句。 WHERE语句是SQL语言中使用频率很高的一种语句。它的作用是从数据库表中选择一些特定的记录行来进行操作。WHE…

第二证券:沪指涨近1%收复2800点,券商等板块拉升,稀土板块爆发

7日早盘&#xff0c;两市股指延续昨日强势&#xff0c;再度拉升。沪指涨近1%克复2800点&#xff0c;深成指、科创50指数大涨约3%&#xff1b;两市半日成交超6000亿元&#xff0c;北向资金净买入超20亿元。 截至午间收盘&#xff0c;沪指涨0.91%报2814.89点&#xff0c;深成指涨…

第1章 认识Flask

学习目标 了解Flask框架&#xff0c;能够说出Flask框架的发展史以及特点 熟悉隔离Python环境的创建方式&#xff0c;能够独立在计算机上创建隔离的Python环境 掌握Flask的安装方式&#xff0c;能够独立在计算机上安装Flask框架 掌握PyCharm配置隔离环境的方式&#xff0c;能…

电脑文件误删除怎么办?8个恢复软件解决电脑磁盘数据可能的误删

您是否刚刚发现您的电脑磁盘数据丢失了&#xff1f;不要绝望&#xff01;无论分区是否损坏、意外格式化或配置错误&#xff0c;存储在其上的文件都不一定会丢失到数字深渊。 我们已经卷起袖子&#xff0c;深入研究电脑分区恢复软件的广阔领域&#xff0c;为您带来一系列最有效…

如何在 emacs 上开始使用 Tree-Sitter (archlinux)

文章目录 如何在emacs上开始使用Tree-Sitter&#xff08;archlinux&#xff09; 如何在emacs上开始使用Tree-Sitter&#xff08;archlinux&#xff09; 在archlinux上使用比windows上不知道要方便多少倍&#xff01; $ sudo pacman -S emacs $ sudo pacman -S tree-sitter这里…

国内首个openEuler师训营圆满结营! 麒麟信安助力培养国产操作系统高质量师资人才

2024年1月22日&#xff0c;全国首个openEuler师训营圆满结营&#xff01;旨在深化产教融合&#xff0c;加速开源教育走进高校&#xff0c;提高师资队伍openEuler专业能力及实践教学水平。 本次师训营由长沙市大数据产业链、长沙市新一代自主安全计算系统产业链指导&#xff0c…