C++之打造my vector篇

news2024/12/23 18:43:44

目录

前言

1.参照官版,打造vector的基本框架

2.丰富框架,实现接口方法

基本的迭代器实现

数据的[]访问

容量和数据空间的改变

vector空间大小的返回与判空

数据的增删

数据打印

拷贝构造和赋值重载

3.扩展延伸,深度理解代码

迭代器失效问题

使用memcpy的拷贝问题

结束语


前言

前面章节我们讲解了vector相关接口,方法的使用,本节内容我们将自己创造vector,模仿官方的接口方法。

1.参照官版,打造vector的基本框架

通过查看官方文档我们知道,vector是个可以变化的数组,是个容器,可以储存一系列数据,

是典型的模版类。

且有三个基本成员start,finish,end_of_storage,我们可以理解为指向数组的开端,数据的结尾,以及容量的结束指针。

上图为插入成员后的分布情况。

故创造了一下的基本框架,因为是我们自己的实现,所以定义了一个my_vector的命名空间,

namespace my_vector {
	template<class T>
	class vector {
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		vector() 
		{}

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

这里我们采用初始化列表来进行默认构造,直接使用私有成员的缺省值,较为简便。

C++11前置生成默认构造

vector()=default;

2.丰富框架,实现接口方法

基本的迭代器实现

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

就是比较简单的返回开始和结束的指针。

数据的[]访问

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 reserve(size_t n) {
	if (n > capacity()) {
		size_t oldsize = size();
		T* temp = new T[n];
       memcpy(tmp, _start, old_size * sizeof(T));
/*
		for (size_t i = 0; i < oldsize; i++) {
			temp[i] = _start[i];
		}
*/
		delete[]_start;
		_start = temp;
		_finish =temp + oldsize;
		_end_of_storage = temp+n;
	}
}

对于扩容操作,我们创建了一个新的数组,然后定义一个oldsize来存储以前的数据空间,来确定之后的_finish位置,因为我们在之前释放了原来的数组了,如果不想这样操作,不想定义一个oldsize,就要把delete操作放在_finish=temp+size()之后。

这里用了memcpy函数来转移数据,但是我们会发现有一个小小的问题,当数据类型为string时,程序会崩溃,这个后续会讲解的。

void resize(size_t n, T val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				reserve(n);
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

resize函数的作用是调整容器的大小,使其能够容纳n个元素。如果n小于当前容器的大小,则容器会被截断;如果n大于当前容器的大小,则容器会被扩展,并且新增的元素会被初始化为val的值。

T val = T():表示一个默认参数,它是容器的元素类型T的默认构造函数生成的对象。如果调用resize时没有指定这个参数,就会使用元素类型的默认值。

如果n小于当前容器的大小
_finish = _start + n;:这条语句会截断容器,使其大小变为n。这里_start是指向容器第一个元素的指针,_finish是指向容器最后一个元素的下一个位置的指针。通过将`_finish`向前移动到_start + n的位置,容器的大小就被减少了。
 如果`n`大于或等于当前容器的大小
- reserve(n);:调用reserve函数确保容器的容量至少为n。如果当前容量小于n,reserve会重新分配内存以容纳至少n个元素。
- 默认构造函数`T()`必须存在,以便能够为新元素提供默认值。如果`T`没有默认构造函数,则这段代码在尝试使用默认参数时会出错。

vector空间大小的返回与判空

size_t size()const {
	return _finish - _start;
}
size_t capacity()const {
	return _end_of_storage - _start;
}
bool empty()const {
	return _start == _finish;
}

大小返回就是几个指针加减即可

数据的增删

对于数据的增删,模拟尾插,尾删,指定位置的插入,删除(后两者都与迭代器iterator相结合)

void push_back(const T& x) {
	if (_finish == _end_of_storage) {
		reserve(capacity() == 0 ? 4 : capacity() * 2);
	}
	*_finish = x;
	++_finish;
}
void pop_back() {
	assert(!empty());
	--_finish;
}
void erase(iterator pos) {
	assert(pos >= _start);
	assert(pos <= _finish);
	iterator it = pos + 1;
	while (it != end()) {
		*(it - 1) = *it;
		it++;
	}
	_finish--;
}
iterator insert(iterator pos,const T&x) {
	assert(pos >= _start  && pos<=_finish);
	if (_finish == _end_of_storage) {
		size_t len = pos - _start;
		reserve(capacity() == 0 ? 4 : capacity() * 2);
		pos = _start + len;
	}
	iterator end = _finish + 1;
	while (end > pos) {
		*end = *(end - 1);
		end--;
	}
	*pos = x;
	_finish++;
	return pos;
}

对于插入数据的操作都要进行扩容的判断操作,对于数据的挪动我们可以采用依次赋值,就像代码中的*end=*(end-1);end--//*(it-1)=*it;it++;

通过画图可以更加清楚的理解。

数据打印

template<class T>
void print_vector(const vector<T>& v) {
	//typename vector<T>::const_iterator it = v.begin();
	auto it = v.begin();
	while (it != v.end()) {
		cout << *it << " ";
		it++;
	}
	cout << '\n';
    for  (auto e:v) {
		cout << e << " ";
	}
	cout << endl;
}
template<class Container>
void print_container(const Container& v) {
	auto it = v.begin();
	while (it != v.end()) {
		cout << *it << " ";
		it++;
	}
	cout << '\n';
	for (auto e : v) {
		cout << e << " ";
	}
	cout << endl;
}

规定,没有实例化的类模板里面取东西,编译器不能区分这里const_iterator是类型还是静态成员变量,所以在注释的部分我们看见前面加了个typename.

//typename vector<T>::const_iterator it = v.begin();

print_vector 函数专门用于打印 std::vector<T> 类型的容器。

print_container 函数是一个更通用的模板函数,可以用于打印任何符合容器概念的类型。

 

void vector5()
{
	vector<string> v;
	v.push_back("11111111111111111111");
	v.push_back("11111111111111111111");
	v.push_back("11111111111111111111");
	v.push_back("11111111111111111111");
	print_container(v);

	v.push_back("11111111111111111111");
	print_container(v);
}

拷贝构造和赋值重载

vector(const vector<T>& v) {
reserve(v.size());
for (auto e : v) {
	push_back(e);
}
}
vector<T>operator=(const vector<T>& v) {
if (this != v) {
	reserve(v.size());
	for (auto e : v) {
		push_back(e);
	}
}
return this;
		}

这一种思路是开设新空间,然后将数据一个个尾插到创建的对象中。

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& operator=(vector v)
		vector<T>& operator=(vector<T> v)
		{
			swap(v);

			return *this;
		}

这种思路是交换的方式,通过调用官方库中的函数,指针交换,将v的地址给了创建的对象。

template <class InputIterator>
vector(InputIterator first, InputIterator last) {
	while (first != last) {
		push_back(*first);
		first++;
	}
}

通过传需要拷贝的对象的数据范围给新对象(迭代器区间),是个函数模版,可以用任意的迭代器初始化,类型匹配即可 。

模板构造函数,用于构造一个std::vector,该构造函数接受两个迭代器firstlast,它们定义了要复制到新vector中的元素的范围。

3.扩展延伸,深度理解代码

在VS环境下,比较严格,在迭代器方面比较严格,特别是失效迭代器的访问。

迭代器失效问题

在测试接口的过程中,有个bug就是迭代器失效问题

我们知道迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T*

因此迭代器失效,实际就是迭代器 底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即 如果继续使用已经失效的迭代器,程序可能会崩溃)。

1.对vector进行扩容操作,像resize,reserve等操作

还有就是在insert,push_back操作过程中涉及了扩容

#include <iostream>
using namespace std;
#include <vector>
int main()
{
    vector<int> v{ 1,2,3,4,5,6 };
    auto it = v.begin();
    // 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
   // v.resize(100, 8);
    // reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容
    //量改变
         v.reserve(100);
         // 插入元素期间,可能会引起扩容,而导致原空间被释放
        v.insert(v.begin(), 0);
          v.push_back(8);
         // 给vector重新赋值,可能会引起底层容量改变
        //v.assign(100, 8);
    /*
   出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释
   放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块
   已经被释放的空间,而引起代码运行时崩溃。
   解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给
   it重新赋值即可。
   */
    while (it != v.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    return 0;
}

修改后的代码 

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v{ 1,2,3,4,5,6 };
    // 在修改vector之后,重新获取迭代器
    auto it = v.begin();
    
    v.reserve(100);
    v.insert(v.begin(), 0);
    v.push_back(8);
    // 如果使用v.assign(100, 8);,也需要在之后重新获取迭代器

    // 重新获取迭代器,因为之前的操作可能会改变vector的内存
    it = v.begin();

    while (it != v.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    return 0;
}

还有一点就是insert数据过后,即使没有扩容,指向容器中插入点之后的所有迭代器、指针和引用都可能失效。

所以当我们继续访问修改p的位置数据,已经失效了,需要更新失效的迭代器。

由于数据挪动,p的指向改变了,所以我们认为迭代器也失效了。

v.insert(p, 40);

p=v.insert(p, 40);
(*(p+1)) *= 10; 

 2.erase的删除导致的迭代器失效问题

	void test_vector3()
	{
		std::vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);

		print_container(v);

		// 删除所有的偶数
		auto it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				v.erase(it);
			}
			else
			{
				++it;
			}
		}

		print_container(v);
	}
}

当我们用VS std中的接口时,会发现直接报错

所以我们也要进行重新的更新

it=v.erase(it); 

#include <iostream>
using namespace std;
#include <vector>
    int main()
    {
        int a[] = { 1, 2, 3, 4 };
        vector<int> v(a, a + sizeof(a) / sizeof(int));
        // 使用find查找3所在位置的iterator
        vector<int>::iterator pos = find(v.begin(), v.end(), 3);
        // 删除pos位置的数据,导致pos迭代器失效。
        v.erase(pos);
        cout << *pos << endl; // 此处会导致非法访问
        return 0;
    }

使用memcpy的拷贝问题

当我们想拷贝几个字符串时,就会出现问题了。

 问题分析:

1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中。

2. 如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。

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

当我们使用这个memcpy版本进行扩容插入时,程序会出现问题

测试代码

void vector5() {
	vector<string> v;
	v.push_back("11111111111111111111");
	v.push_back("11111111111111111111");
	v.push_back("11111111111111111111");
	v.push_back("11111111111111111111");
	print_container(v);

	v.push_back("11111111111111111111");
	print_container(v);
}

memcpy是浅拷贝,temp和原来的v指向了同一块空间,当调用了delete[]时,11111111...字符串被析构了,空间释放,变成随机值,后面又delete,free _start ,这时候temp指向的是释放的空间。

所以我们可以调用赋值,就可以解决问题,本质调用string的赋值,其他类型赋值一样的。

旧空间释放就不会影响新空间。

for (size_t i = 0; i < oldsize; i++) {
            temp[i] = _start[i];

4.完整代码复现

#pragma once
#include <iostream>
#include <assert.h>
#include <vector>
#include <string>
using namespace std;
namespace my_vector {
	template<class T>
	class vector {
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		vector()
		{}

		vector(const vector<T>& v) {
			reserve(v.size());
			for (auto e : v) {
				push_back(e);
			}
		}
		/*
		vector<T>operator=(const vector<T>& v) {
			if (this != v) {
				reserve(v.size());
				for (auto e : v) {
					push_back(e);
				}
			}
			return this;
		}
		*/
		template <class InputIterator>
		vector(InputIterator first, InputIterator last) {
			while (first != last) {
				push_back(*first);
				first++;
			}
		}
		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& operator=(vector v)
		vector<T>& operator=(vector<T> v)
		{
			swap(v);

			return *this;
		}

		void clear() {
			_finish = _start;
		}
		~vector() {
			if (_start) {
				delete[]_start;
				_start = _finish = _end_of_storage = nullptr;
			}
		}
		iterator begin() {
			return _start;
		}
		iterator end() {
			return _finish;
		}
		const_iterator begin()const {
			return _start;
		}
		const_iterator end() const {
			return _finish;
		}
		void reserve(size_t n) {
			if (n > capacity()) {
				size_t oldsize = size();
				T* temp = new T[n];
				memcpy(temp, _start, oldsize*sizeof(T));
				/*
				for (size_t i = 0; i < oldsize; i++) {
					temp[i] = _start[i];
				}
				*/
				delete[]_start;
				_start = temp;
				_finish = temp + oldsize;
				_end_of_storage = temp + n;
			}
		}
		void resize(size_t n, T val = T()) {
			if (n < size()) {
				_finish = _start + n;
			}
			else {
				reserve(n);
				while (_finish < _start + n) {
					*_finish = val;
					_finish++;
				}
			}

		}
		void push_back(const T& x) {
			if (_finish == _end_of_storage) {
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			*_finish = x;
			++_finish;
		}
		void pop_back() {
			assert(!empty());
			--_finish;
		}
		void erase(iterator pos) {
			assert(pos >= _start);
			assert(pos <= _finish);
			iterator it = pos + 1;
			while (it != end()) {
				*(it - 1) = *it;
				it++;
			}
			_finish--;
		}
		iterator insert(iterator pos, const T& x) {
			assert(pos >= _start && pos <= _finish);
			if (_finish == _end_of_storage) {
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;
			}
			iterator end = _finish + 1;
			while (end > pos) {
				*end = *(end - 1);
				end--;
			}
			*pos = x;
			_finish++;
			return pos;
		}
		size_t size()const {
			return _finish - _start;
		}
		size_t capacity()const {
			return _end_of_storage - _start;
		}
		bool empty()const {
			return _start == _finish;
		}
		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 = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};
	template<class T>
	void print_vector(const vector<T>& v) {
		//typename vector<T>::const_iterator it = v.begin();
		auto it = v.begin();
		while (it != v.end()) {
			cout << *it << " ";
			it++;
		}
		cout << '\n';
		for (auto e : v) {
			cout << e << " ";
		}
		cout << endl;
	}
	template<class Container>
	void print_container(const Container& v) {
		/*
		auto it = v.begin();
		while (it != v.end()) {
			cout << *it << " ";
			it++;
		}
		cout << '\n';
		*/
		for (auto e : v) {
			cout << e << " ";
		}
		cout << endl;
	}
	void vector1() {
		vector<int>v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		v.push_back(6);
		int x;
		cin >> x;
		auto p = find(v.begin(), v.end(), x);
		if (p != v.end()) {
			p = v.insert(p, 40);
			(*(p + 1)) *= 10;
		}

		//v.pop_back();
		//v.pop_back();
		//v.insert(v.begin() + 2, 5);

		//v.erase(v.begin() + 3);
		for (size_t i = 0; i < v.size(); i++) {
			cout << v[i] << endl;
		}

	}
	void vector2() {
		vector<int>v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		v.push_back(6);
		print_vector(v);
		vector<int>v1(v);
		vector <int>v2 = v;
		vector<int> v3(v1.begin(), v1.begin() + 3);
		print_container(v1);
		print_container(v2);
		print_container(v3);
		/*
		vector<double>v1;
		v1.push_back(1.1);
		v1.push_back(2.2);
		v1.push_back(3.3);
		v1.push_back(4.4);
		v1.push_back(5.5);
		v1.push_back(6.6);
		print_container(v1);
		*/
	}
	void vector3() {
		vector<int> v;
		v.resize(10, 1);
		v.reserve(20);

		print_container(v);
		cout << v.size() << endl;
		cout << v.capacity() << endl;

		v.resize(15, 2);
		print_container(v);

		v.resize(25, 3);
		print_container(v);

		v.resize(5);
		print_container(v);
	}
	void vector4() {
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(4);
		v.push_back(5);
		print_container(v);

		// 删除所有的偶数
		auto it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				v.erase(it);
			}
			else {//不加else,不会删除连续的偶数,会++两次
				++it;
			}

		}

		print_container(v);
	}
	void test_vector3()
	{
		std::vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);

		print_container(v);

		// 删除所有的偶数
		auto it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				it=v.erase(it);
			}
			else
			{
				++it;
			}
		}

		print_container(v);
	}
	void vector5() {
		vector<string> v;
		v.push_back("11111111111111111111");
		v.push_back("11111111111111111111");
		v.push_back("11111111111111111111");
		v.push_back("11111111111111111111");
		print_container(v);

		v.push_back("11111111111111111111");
		print_container(v);
	}
}

结束语

本期博客就到此结束啦,相信通过自己对vector的实现,大家对vector有了更深的了解。

最后希望友友们给小编点点赞吧,感谢各位友友的支持!!! 

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

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

相关文章

iText2KG:显著降低LLM构建知识图谱时的幻觉现象

1. 当前知识图谱构建存在的问题 知识图谱通过捕捉实体之间的关系来构建知识的结构化表示&#xff0c;在分析文本数据集和从结构化异构数据中推断知识方面具有显著优势。比如&#xff0c;知识图谱能够融合来自多个来源的不同数据&#xff0c;提供一个具有凝聚力的信息视角。还能…

【Python进阶】学习Python从入门到进阶,详细步骤,就看这一篇。文末附带项目演练!!!

详细的Python学习路线 1. Python基础 Python安装和环境配置&#xff1a;学习如何在你的操作系统上安装Python&#xff0c;并配置开发环境。变量和数据类型&#xff1a;学习如何定义变量&#xff0c;以及Python中的基本数据类型&#xff0c;如整数、浮点数、字符串等。 Pytho…

【人工智能学习笔记】4_3 深度学习基础之循环神经网络

循环神经网络(Recurrent Neural Network, RNN) 是一类以序列(sequence)数据为输入,在序列的演进方向进行递归(recursion)且所有节点(循环单元)按链式连接的递归神经网络(recursive neural network),循环神经网络具有短期记忆能力 RNN核心思想 RNN的结构 一个典型…

基于CNN-BiLSTM-Attention的流量预测 完整数据代码可直接运行

直接看视频: 基于CNN-BiLSTM-Attention的流量预测 完整数据代码可直接运行_哔哩哔哩_bilibili 模型: 有效提取径流时间序列的信息特征,提高径流预测模型的高维非线性拟合能力和预测性能的稳定性,将卷积神经网络(CNN),双向长短期记忆网络(BiLSTM)和注意力机制(attention)相…

tomcat端口被占用解决方法

在安装目录的conf下修改server.xml文件&#xff0c;修改后保存重启即可

十四、MySQL高级— 分库分表(7)

&#x1f33b;&#x1f33b; 目录 一、分库1.1 修改配置 schema.xml1.2 如何选择分库表1.3 SQLyog 连接 mycat 二、水平分表2.1 schema.xml2.2 rule.xml2.3 跨库join2.3.1 ER表2.3.2 全局表 2.4 全局序列2.4.1 本地文件2.4.2 数据库方式(一般都用这个)2.4.3 时间戳方式2.4.4 自…

时间序列预测学习方向总概括

推荐资源&#xff1a; 1.MA、AR、ARIMA 算法小陈-CSDN博客 2.informer论文讲解 【2024最火的两个模型&#xff1a;InformerLSTM两大时间序列预测模型&#xff0c;论文精读代码复现&#xff0c;究极通俗易懂&#xff01;——人工智能|AI|机器学习|深度学习-哔哩哔哩】 https…

微波无源器件 4 基于高阶定向耦合器的双极化波束形成网络

摘要&#xff1a; 一种Ka频段的双极化3dB定向耦合器被设计用于波束形成网络应用。所提出的解决方案对于紧凑Nolen网络。Nolen结构优于器平面和无损特别具有吸引力。两个平行方波导通过口径阵列耦合&#xff0c;设计用于获得两个正交极化之间的所需耦合和高隔离度。 索引词&…

sql语句的训练2024/9/9

1题 需要看清思路&#xff1a;不是将数据库中的device_id的名字改为user_infors_example&#xff0c;而是在查找的时候&#xff0c;需要将device_id看成user_infors_example来进行查找。 答案 select device_id AS user_infos_example FROM user_profile limit 2 2 当固定查找…

idea报错:java:错误:不支持发行版本5

问题 使用idea创建Maven项目运行是报错&#xff1a;java&#xff1a;错误&#xff1a;不支持发行版本5 解决 1.打开Settings 2.在Java compiler 里面修改和Java版本一致 然后就可以正常运行

租房市场新动力:SpringBoot大学生租房系统

第1章 绪论 1.1 课题背景 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。所以各行业&#xff0c;尤其是规模较大的企业和学校等…

erlang学习: Mnesia Erlang数据库2

Mnesia数据库增加与查询学习 -module(test_mnesia).-record(shop, {item, quantity, cost}). -record(cost, {name, price}). -record(design, {info, plan}). %% API -export([insert/3,select/1,start/0]). start() ->mnesia:start().insert(Name, Quantity, Cost) ->…

大模型之三十一-音源分离

大模型之三十一-音乐分离模型 因为TTS模型训练还有几个结果没出&#xff0c;本篇先介绍一下音乐分离模型吧。其实可能你也猜到了&#xff0c;一部分TTS的数据是网上爬来的&#xff0c;这种音频可能会有背景音之类的&#xff0c;这里需要将乐器类的伴奏去掉。所以就此介绍一下本…

U盘格式化怎么办?这4款软件可以帮你进行数据恢复。

如果你的U 盘被格式化&#xff0c;里面的数据就会被清除掉了。有备份的话&#xff0c;就不用担心丢失那些重要的数据&#xff1b;如果没有备份&#xff0c;也有办法解决&#xff1b;可以用电脑自带的一些功能恢复&#xff0c;或者是使用专业的恢复软件。如果大家有需求&#xf…

【软考】信息安全

【软考】信息安全 一.信息安全基础知识 信息安全是保障信息系统和数据的保密性、完整性、可用性、可控性和可追溯性的综合措施。这五个要素是信息安全的基础&#xff0c;缺一不可。 1. 保密性 (Confidentiality) 定义: 保证信息只被授权人员访问。举例: 银行账户信息、医疗…

【JAVA】Tomcat性能优化、安全配置、资源控制以及运行模式超详细

文章目录 一、Tomcat性能优化application.yml配置maxThreads 连接数限制压缩传输AJP禁用 二、JVM方向优化设置并行垃圾回收器查看gc日志文件 三、Tomcat安全配置入侵防范禁用非法HTTP请求方法禁止目录列出防止恶意关闭服务配置HTTPS加密协议HttpOnly标记安全头配置 四、Tomcat资…

Rancher 与 Kubernetes(K8s)的关系

1. 简介 1.1 Kubernetes 作为容器编排平台 Kubernetes 是一个开源平台&#xff0c;用于自动化部署、扩展和管理容器化的应用。它提供了容器调度、自动伸缩、健康检查、滚动更新等功能。 例子&#xff1a;假设您有一个微服务架构的应用程序&#xff0c;需要运行在多个节…

基于arcpro3.0.2版的使用深度学习目标提取之建筑房屋

基于arcpro3.0.2版的使用深度学习目标提取之建筑房屋 采用像素分类方法&#xff0c;像素分类一般把多边形详细轮廓给标注出来&#xff0c; 而目标检测就标注出对象大致矩形框就行, 本次训练结果&#xff1a;采用GPU显卡Nivda 1080 训练模型图 20个周期GPU训练 &#xff08;一…

【JavaEE】TCP协议 (TCP-传输层协议 万字详解)

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【Java】登神长阶 史诗般的Java成神之路 &#x1f3a4;一.报头格式 TCP (Transmission Control Protocol) 是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP 被设计用来提供端到端的数据传…