C++——string

一学习string的原因

1.从个人理解角度上:

在刚开始学习之前,我只知道学习完string在以后的刷题中能提高做题效率,在对字符串的处理string库中也许有对应的接口去实现需求,不用自己去写函数的实现。

但在学string中改变了之前的看法:不仅是要会用接口,而且在理解了接口的底层原理后能更好的去理解,使用它。

总结:使用——明理——扩展

2从C语言上:

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

二标准库中的string

使用string必须用它对应的头文件:#include<string>

在学习接口及其文档的说明,可以到cpulspuls官网中去学习:

cplusplus.com - The C++ Resources Network

1(拷贝)构造和析构

string()     构造string类的空字符串(其中有’\0‘)

string(char* s)   构造的同时进行string的拷贝(支持单参数隐式类型转换)

string(const string& str)     string拷贝构造

string(const string& str , size_t  pos , size_t  len = npos)     进行str子串的获取,pos是子串的起始位置,len是子串的结束位置(没有说明就默认是str的结束位置)

~string()     string的析构

2运算符重载

使string的访问向数组访问数据一样数组名+[]

char& operator[] (size_t pos)     

const char& operator[] (size_t pos) const   

 string类的赋值,对象可以是string,字符串,字符

string& operator = (const string& str)   

string& operator = (const char* s)

string& operator = (char c)

string类字符串的尾插,对象可以是string,字符串,字符

string& operator += (const string& str)

string& operator += (const char*s)

string& operator += (char c)

实现string类与char*,char类型的字符(串)进行拼接(反过来也成立) 

string operator+ (const string& lhs , const char* rhs)

string operator+ (const char* lhs , const string& rhs)

string operator+ (const string& lhs , char rhs)

string operator+ (char rhs ,  const string& lhs)

流插入,流提取的重载

istream& operator >>(istream is , string& str)

ostream& operator <<(ostream os , string& str)

3迭代器

迭代器行为指针一样,但不=指针

iterator begin()     指向string的开始 

const_iterator begin() const   

iterator end()      指向string的末尾

const_iterator end() const

反向迭代器

iterator rbegin()     指向string的末尾

irerator rend()        指向string的开始

有了迭代器,我们就有了两种遍历string的方式:迭代器与范围for(本质是迭代器)

#include<string>
int main()
{
	string s1("hello warld");
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << ' ';
		it++;
	}
	cout << endl;
	for (auto ch : s1)
	{
		cout << ch << ' ';
	}
	cout << endl;
	return 0;
}

2查询string的各种数据

size_t size() const     返回string内的字符个数(不包含’\0‘)      =size_t length() const

size_t capacity() const     返回string的空间大小(包含’\0‘)

void reserve(size_t n=0)   开n个空间大小

(知道要插入的多少数据提前开好空间就不用进行扩容,提高了效率)

(但空间不够时,string应该要进行对应的空间扩容。那么是要扩容多少呢?2倍扩?

我们用+=的方式在string末尾每次插入一个字符,循环100次来观察capacity的变化:

#include<string>
int main()
{
	string s;

	size_t sz = s.capacity();
	cout << "capacity changed: " << sz << '\n';
	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s += ('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
	return 0;
}

在VS底层中实现的扩容:先开出15内存空间,等到空间不够时,第一次扩容是2倍扩,接下来的扩容都是大概按1.5来扩容。

而在g++下的扩容全是按照2倍扩容来的:

但两者的扩容方式不同会不会有什么影响呢?

没有!! 2倍扩容也好1.5倍扩容也好,说到底它们的目标都是要进行扩容。实现起来的细节是不做考虑的。


void resize(size_t n,char c)     开n个空间大小并插入n-size()个字符c

resize一般不缩容,在以下的不同情况使用resize会有不同的效果:

5增删查改

viod push_back (char c)     在string的末尾插入一个字符

void pop_back (char c)       在string的末尾删除一个字符        

string& insert(size_t pos , char c)     在pos下插入字符(字符串,string)

string& erase(size_t pos , size_t len = npos)     在pos位置下删除len个字符(不加len默认删除pos后面的全部字符)  (左闭右开)

void swap(string& str)   不仅仅是数据的交换,还有size,capacity的交换

6其它操作

void swap(string& x , string& y)     与算法库的swap分离开(如果调用算法库的swap,三次拷贝+一次析构代价太大)

const char* c_str() const     类型转换成char*方便打印(string类的要用到运算符重载)

string substr(size_t pos = 0 , size_t len = npos)     从pos位置到len个位置的字符串截取

size_t find(char c , size_t pos=0)   返回字符c所在的下标

string& replace(size_t pos , size_t len = npos , char c)     在pos到len的位置插入c字符

两者的特性结合可用来解决:

字符串空格的替换

三string的模拟实现

在string类中共有三个成员:_str(字符串),_size(字符个数),_capacity(空间大小)

其中,_size个数不包含‘\0’,但在_capacity中要多开出一个空间存储‘\0’!!

3.1成员函数

3.1.1构造函数

在实现构造时,如果string构造不传参,默认为空串。

但不意味着给缺省值时就是”\0“,因为写成”“是有’\0‘的存在的

//string(const char* str = "\0")
string(const char* str = "")
	:_size(strlen(str))
{
	_capacity = _size;
	_str = new char[_size + 1];
	strcpy(_str, str);//把字符串拷贝给_str成员对象

}

3.1.2拷贝构造 

 拷贝构造里不单单只是数据拷贝!要在拷贝之前先new一块跟str一样长的空间,在进行数据的拷贝。也就是所谓的深拷贝!

一般的思路是:直接开出一个与拷贝对象一样长的空间,再把对应的数据赋值过去就完事了

string(const string& str)
{
	_str = new char[str._capacity + 1];
	strcpy(_str, str._str);
	_size = str._size;
	_capacity = str._capacity;
}

但这种属于是’自己买菜自己做‘。是比较传统的写法。

有这样的写法:构造一个与拷贝对象一样的临时对象,在进行交换(深拷贝),比较方便简洁:

string(const string& str)
{
	string tmp(str.c_str());
	swap(tmp);
}

 有了拷贝构造,与它对应的还有一个赋值运算符的重载:也是类似的道理:

string& operator=(const string& str)
{
	char* tmp = new char[str._capacity + 1];
	strcpy(tmp, str._str);

	delete[] _str;//释放
	_str = tmp;
	_size = str._size;
	_capacity = str._capacity;
    return *this;
}

//现代写法
string& operator=(const string& str)
{
	string tmp(str.c_str());
	swap(tmp);
    return *this;
}

string& operator=(string ss)
{    
    swap(ss);
    return *this;
}

3.1.3析构函数

~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

3.2迭代器

实现的迭代器因为要支持可读可写与只读两个版本,实现时要实现俩个版本出来:

对应的begin(),end()实现成string类中的成员函数

typedef char* iterator;//类似指针的作用
typedef const char* const_iterator;
const_iterator begin() const
{
	return _str;
}
const_iterator end() const
{
	return _str + _size;
}

iterator begin()
{
	return _str;
}
iterator end()
{
	return _str + _size;
}

3.3扩容

传参进来的n如果比_capacity要小则需要进行空间的扩容到n+1(’\0‘要开给它):

void reserve(size_t n)
{
	//扩容
	if (_capacity < n)
	{
		char* tmp = new char[n + 1];
		strcpy(tmp , _str);
		_str = tmp;
		_capacity = n;
	}
}

它的扩容不是我们在C语言学的函数realloc扩容那样有两种情况:原地扩与异地扩。它的扩容就是直接看出一块新的空间,在把_str的数据拷贝的! 

3.4插入字符(串)

3.4.1append

将原来的_capacity与拼接形成新的string的_size个数进行比较,空间不够才进行扩容:

void append(const char* s)
{
	size_t len = strlen(s);
	//扩容
	if (_capacity < _size + len)
	{
		reserve(_size + len);
	}
	/*for (size_t i = _size; i < len; i++)
	{
		_str[i] = *s;
		s++;
	}*/
	strcpy(_str +_size, s);
	_size += len;
}

3.4.2insert

在pos位置上插入字符也好,字符串也好,都需要进行是否扩容与数据挪动,字符串的挪动必须是从后往前进行字符串的覆盖:

void insert(size_t pos, char ch)
{
	assert(pos <= _size);
	if (_capacity == _size)
	{
		//扩容
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
    size_t end=_size;
	while (end>=pos)
	{
		_str[end+1] = _str[end];
		end--;
	}
	_str[pos] = ch;
	_size += 1;
}

但在这有个细节:如果pos的位置是0即进行头插,这将会进行程序出现问题:因为但end=0循环进入,end--使得end=-1。但end的类型是size_t,不会为负数!修改方式有两种:

1修改类型;2让end=_size+1进行循环时end永远都>pos:

	void insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_capacity == _size)
		{
			//扩容
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
        1
		size_t end = _size;//当pos=0时,size_t end=0,end--!=-1
		int end = _size;
		while (end >= (int)pos)
		{
			_str[end + 1] = _str[end];
			end--;

        2
		size_t end = _size + 1;
		while (end>pos)
		{
			_str[end] = _str[end - 1];
			end--;
		}

		_str[pos] = ch;
		_size += 1;
	}

插入的是字符串也是同理的:

		void insert(size_t pos, const char* str)
		{
			size_t len = strlen(str);
			assert(pos <= _size);
			if (_capacity <= _size + len)
			{
				//扩容
				reserve(_size + len);
			}
			/*int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];
				end--;
			}*/
			size_t end = _size + len;
			while (end > pos+len-1)
			{
				_str[end] = _str[end-len];
				end--;
			}
			strncpy(_str+pos, str, len);
			_size += len;
		}

 3.4.3push_back

复用insert进行尾插或者直接实现:

void push_back(char ch)
{
	if (_capacity == _size)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	_str[_size++] = ch;
	_str[_size] = '\0';
	//insert(_size, ch);
}

3.4.4赋值运算符=重载

string& operator+=(char ch)
{
	push_back(ch);
	return *this;
}
string& operator+=(const char* str)
{
	append(str);
	return *this;
}

3.5resize

实现resize是要了解的是:使用resize一般不缩容。要插入n个字符是有两种情况:n<=_size是仅需保留前n个字符串后加’\0‘即可;n>_capacity就要进行扩容与插入n-_size个字符:

		void resize(size_t n, char ch = npos)
		{
			if (n <= _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				//扩容
				reserve(n);//n>size后面有要求加字符
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
			}
		}

对应resize与reserve:基本上都是有扩容的功能的,但resize能在进行扩容的基础上将数据进行初始化,比较适合在对数据的管理上使用它。

3.6find

进行string的遍历,如果找到该字符就返回该字符的下标:如果是找字符串,找到了就返回该字符串的起始位置:

size_t find(char c, size_t pos = 0) const
{
	assert(pos < _size);
	for (size_t i = pos; i < _size; i++)
	{
		if (c == _str[i])
		{
			return i;
		}
	}
	return npos;
}
size_t find(const char* s, size_t pos = 0) const
{
	assert(pos < _size);
	const char* p=strstr(_str + pos, s);
	if(p)
	{
		return p - _str;//指针-指针=s的下标
	}
	else
	{
		return npos;
	}
}

 3.7运算符重载<<,>>

流提取或者是流插入重载成全局函数好方便调用:

ostream& operator<< (ostream& os, const string& str)
{
	for (int i = 0; i < str.size(); i++)
	{
		os << str[i];
	}
	return os;
}

istream& operator>>(istream& in, string& s)
{
	s.clear();//防止string中有数据影响流插入
	char ch;
	//in >> ch;
	ch = in.get();
	//s.reserve(128);
	while (ch != '\n' && ch != ' ')
	{
		s += ch;
		ch = in.get();
	}

	return in;
}

在重载流插入时,如果按照上面的实现方式的话,在每次插入时都要进行扩容,非常影响程序运行的效率。

如果要进行reserve开空间,但至于要看多少不能确定。所以就有人进行对这段代码的优化: 

istream& operator>>(istream& in, string& s)
{
	s.clear();

	char ch;
	//in >> ch;
	ch = in.get();
	char buff[128];
	size_t i = 0;
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		// [0,126]
		if (i == 127)
		{
			buff[127] = '\0';
			s += buff;
			i = 0;
		}

		ch = in.get();
	}

	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}

	return in;
}

要不要扩容取决于会不会超出buff的数组总个数,非常的妙!!!

完整源代码

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<assert.h>
#include<iostream>
using namespace std;
namespace bit
{
	class string
	{
	public:
		//迭代器
		typedef char* iterator;//类似指针的作用
		typedef const char* const_iterator;
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}

		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}

		//在末尾加n个ch字符
		void resize(size_t n, char ch = npos)
		{
			if (n <= _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				//扩容
				reserve(n);//n>size后面有要求加字符
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
			}
		}

		//先开空间
		void reserve(size_t n)
		{
			//扩容
			if (_capacity < n)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				_str = tmp;
				_capacity = n;
			}
		}

		//末尾插人字符
		void push_back(char ch)
		{
			if (_capacity == _size)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size++] = ch;
			_str[_size] = '\0';
			//insert(_size, ch);
		}
		//末尾插入字符串
		void append(const char* s)
		{
			size_t len = strlen(s);
			//扩容
			if (_capacity < _size + len)
			{
				reserve(_size + len);
			}
			/*for (size_t i = _size; i < len; i++)
			{
				_str[i] = *s;
				s++;
			}*/
			strcpy(_str +_size, s);
			_size += len;
		}
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//在pos位置插入字符
		void insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_capacity == _size)
			{
				//扩容
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//size_t end = _size;//当pos=0时,size_t end=0,end--!=-1
			/*int end = _size;
			while (end >= (int)pos)
			{
				_str[end + 1] = _str[end];
				end--;
			}*/
			size_t end = _size + 1;//size-1X
			while (end>pos)
			{
				_str[end] = _str[end - 1];
				end--;
			}
			_str[pos] = ch;
			_size += 1;
		}
		void insert(size_t pos, const char* str)
		{
			size_t len = strlen(str);
			assert(pos <= _size);
			if (_capacity <= _size + len)
			{
				//扩容
				reserve(_size + len);
			}
			/*int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];
				end--;
			}*/
			size_t end = _size + len;
			while (end >= pos+len)
			{
				_str[end] = _str[end-len];
				end--;
			}
			strncpy(_str+pos, str, len);
			_size += len;
		}

		//删除pos位置的字符
		void erase(size_t pos, int len = npos)
		{
			assert(pos <= _size);
			if (len == npos || len >= _size - pos)//len+pos会溢出
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
		}

		//string(const char* str = "\0")
		string(const char* str = "")
			:_size(strlen(str))
		{
			_capacity = _size;
			_str = new char[_size + 1];
			strcpy(_str, str);//把字符串拷贝给_str成员对象

		}

		//传统写法
		//s2(s1)
		//string(const string& str)
		//{
		//	_str = new char[str._capacity + 1];
		//	strcpy(_str, str._str);
		//	_size = str._size;
		//	_capacity = str._capacity;
		//}
		//s2=s1
		//string& operator=(const string& str)
		//{
		//	char* tmp = new char[str._capacity + 1];
		//	strcpy(tmp, str._str);

		//	delete[] _str;//置空
		//	_str = tmp;
		//	_size = str._size;
		//	_capacity = str._capacity;
		// return *this;
		//}

		//现代写法
		string(const string& str)
		{
			string tmp(str.c_str());
			swap(tmp);
		}
		string& operator=(string str)
		{
			swap(str);
			return *this;
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		const char* c_str() const
		{
			return _str;
		}
		size_t size() const
		{
			return _size;
		}
		size_t capacity() const
		{
			return _capacity;
		}
		char& operator[](size_t pos)
		{
			assert(pos <= _size);
			return _str[pos];
		}
		const char& operator[](size_t pos) const
		{
			assert(pos <= _size);
			return _str[pos];
		}

		void clear()
		{
			_size = 0;
			_str[_size] = '\0';
		}

		//string的成员函数
		void swap(string& str)
		{
			//共有三种swap使用:算法,string.swap,swap
			std::swap(_str, str._str);
			std::swap(_size, str._size);
			std::swap(_capacity, str._capacity);
		}

		bool empty()const
		{
			if (_size == 0)
			{
				return true;
			}
			return false;
		}

		size_t find(char c, size_t pos = 0) const
		{
			assert(pos < _size);
			for (size_t i = pos; i < _size; i++)
			{
				if (c == _str[i])
				{
					return i;
				}
			}
			return npos;
		}
		size_t find(const char* s, size_t pos = 0) const
		{
			assert(pos < _size);
			const char* p=strstr(_str + pos, s);
			if(p)
			{
				return p - _str;//指针-指针=s的下标
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos = 0, size_t len = npos) const
		{
			string s;
			if (len >= _size-pos)
			{
				for (size_t i = pos; i < _size; i++)
				{
					s += _str[i];
				}
			}
			else
			{
				for (size_t i = pos; i < pos + len; i++)
				{
					s += _str[i];
				}
			}
			return s;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	public:
		static const int npos;//类中共有的对象
	};
	const int string::npos = -1;

	bool operator==(const string& s1,const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}
	bool operator<(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}
	bool operator>(const string& s1, const string& s2)
	{
		return !(strcmp(s1.c_str(), s2.c_str()) == 0 || strcmp(s1.c_str(), s2.c_str()) < 0);
	}

	//重载成全局函数
	ostream& operator<< (ostream& os, const string& str)
	{
		for (int i = 0; i < str.size(); i++)
		{
			os << str[i];
		}
		return os;
	}
	istream& operator>> (istream& is, string& str)//getline的实现方式	
	{
		str.clear();
		char ch = is.get();
		char buffer[128];
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			//str += ch;
			buffer[i++] = ch;
			if (i == 127)
			{
				buffer[127] = '\0';
				//开128个空间
				str += buffer;
				//重新计算
				i = 0;
			}
			ch = is.get();
		}
		if (i > 0)
		{
			//有多少看多少
			buffer[i] = '\0';
			str += buffer;
		}
		return is;
	}
	/*istream& operator>> (istream& is, string& str)
	{
		str.clear();
		char ch = is.get();
		while(ch != ' ' && ch != '\n')
		{
			str += ch;
			ch = is.get();
		}
		return is;
	}*/

	//定义一个全局的swap就不会去调用算法的swap
	void swap(string& s1,string& s2)
	{
		s1.swap(s2);
	}

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

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

相关文章

安卓安装Magisk面具以及激活EdXposed

模拟器&#xff1a;雷电模拟器 安卓版本: Android9 文中工具下载链接合集&#xff1a;https://pan.baidu.com/s/1c1X3XFlO2WZhqWx0oE11bA?pwdr08s 前提准备 模拟器需要开启system可写入和root权限 一、安装Magisk 1. 安装magisk 将magisk安装包拖入模拟器 点击&#xff1a…

UnityShader(十六)凹凸映射

前言&#xff1a; 纹理的一种常见应用就是凹凸映射&#xff08;bump mapping&#xff09;。凹凸映射目的就是用一张纹理图来修改模型表面的法线&#xff0c;让模型看起来更加细节&#xff0c;这种方法不会改变模型原本的顶点位置&#xff08;也就是不会修改模型的形状&#xf…

《硬件历险》之Mac抢救出现问题的时间机器硬盘中的数据

本文虽然使用“抢救”一词&#xff0c;但是运气比较好&#xff0c;远没有达到访问和修改底层的信息来抢救的地步。如果你是需要通过访问和修改底层信息来抢救数据&#xff0c;建议阅读刘伟的《数据恢复技术深度揭秘&#xff08;第二版&#xff09;》或者寻找专业人士的帮助。 《…

卷积篇 | YOLOv8改进之C2f模块融合SCConv | 即插即用的空间和通道维度重构卷积

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。SCConv是一种用于减少特征冗余的卷积神经网络模块。相对于其他流行的SOTA方法&#xff0c;SCConv可以以更低的计算成本获得更高的准确率。它通过在空间和通道维度上进行重构&#xff0c;从而减少了特征图中的冗余信息。这种…

留学生课设|R语言|研究方法课设

目录 INSTRUCTIONS Question 1. Understanding Quantitative Research Question 2. Inputting data into Jamovi and creating variables (using the dataset) Question 3. Outliers Question 4. Tests for mean difference Question 5. Correlation Analysis INSTRUCTIO…

深度学习 精选笔记(13.1)卷积神经网络-LeNet模型

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

SAR ADC教程系列5——FFT频谱泄露以及相干采样

频谱泄露的出现以及如何规避&#xff1f; 为什么要相干采样&#xff1f; 1.分析ADC输出信号的频谱工具&#xff1a;DFT&#xff08;Discrete Fourier Transform) 重点&#xff1a;DFT相邻频谱频率间隔为fs/N 如何规避频谱泄露&#xff1f; 对于DFT&#xff0c;它对于接收到的信…

Qt文件读写

做一个简单的文件读写&#xff0c;我们把一个结构体内的数据写入到二进制文件中&#xff0c;并重新读取解析。代码结构如下&#xff1a; 项目名称随便起就好了。main.cpp是主函数&#xff1b;DataHandler实现文件的写与读&#xff0c;还要模拟过程&#xff1b;Definition.h放置…

机器学习-绪论

机器学习致力于研究如何通过计算的手段、利用经验来改善系统自身的性能。在计算机系统中&#xff0c;“经验”通常以“数据”的形式存在&#xff0c;因此&#xff0c;机器学习所研究的主要内容&#xff0c;是关于在计算机上从数据中产生“模型”的算法&#xff0c;即“学习算法…

NPM 仓库的超集 JSR 来了!

引言 今天在 Deno 博客中看到了一篇文章&#xff0c;介绍了一个叫 JSR 的包管理注册中心&#xff0c;简单尝试了一下觉得还不错&#xff0c;本文将结合原文章和个人体验对 JSR 进行一个详细的介绍。 在现如今的前端开发中&#xff0c;包管理注册中心 (如 npmjs.com) 扮演着至…

Git——本地使用详解

目录 Git1、开始版本控制1.1、初始化Repository1.2、使目录脱离Git控制 2、把文件交给Git管控2.1、创建文件后交给Git2.2、git add之后再次修改文件2.3、git add "--all"与"."参数区别2.4、把暂存区的内容提交到存储库里存档 3、工作区、暂存区与存储库3.1…

Epuck2机器人固件更新及IP查询

文章目录 前言一、下载固件更新软件包&#xff1a;二、查询机器人在局域网下的IP 前言 前面进行了多机器人编队仿真包括集中式和分布式&#xff0c;最近打算在实物机器人上跑一跑之前的编队算法。但由于Epuck2机器人长时间没使用&#xff0c;故对其进行固件的更新&#xff0c;…

软考高级:软件架构本质和作用概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

【PyTorch】进阶学习:一文详细介绍 torch.save() 的应用场景、实战代码示例

【PyTorch】进阶学习&#xff1a;一文详细介绍 torch.save() 的应用场景、实战代码示例 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程…

Vue组件封装方案对比——v-if方式与内置component方式

近期在准备搭建一个通用组件库&#xff0c;而公司现有的各个系统也已有自己的组件库只是没抽离出来&#xff0c;但是目前有两套不同的组件封装方案&#xff0c;所以对于方案的选择比较困惑&#xff0c;于是对两种方式进行了对比&#xff0c;结合网上找到的一些开源组件库进行分…

wireshark解析https数据包

Debian11环境&#xff1a; 在linux环境下抓取访问某个https的网址时抓取的数据包都是加密的&#xff0c;导致无法跟踪到数据包流&#xff0c;现在尝试将抓取的https包进行解密。 1、解密https数据包需要设置SSLKEYLOGFILE变量&#xff0c;推荐写入配置文件中。 echo "exp…

Mysql的行级锁

MySQL 中锁定粒度最小的一种锁&#xff0c;是 针对索引字段加的锁 &#xff0c;只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小&#xff0c;并发度高&#xff0c;但加锁的开销也最大&#xff0c;加锁慢&#xff0c;会出现死锁。行级锁和存…

Ps:文字工具

工具箱里的文字工具组中包含了四种工具&#xff1a; 横排文字工具 Horizontal Type Tool 直排文字工具 Vertical Type Tool 横排文字蒙版工具 Horizontal Type Mask Tool 直排文字蒙版工具 Vertical Type Mask Tool 快捷键&#xff1a;T 横排文字蒙版工具和直排文字蒙版工具…

C++第六弹---类与对象(三)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、类的6个默认成员函数 2、构造函数 2.1、概念 2.2、特性 3、析构函数 3.1、概念 3.2、特性 3.3、调用顺序 总结 1、类的6个默认成员函数…

力扣hot100:33. 搜索旋转排序数组(二分的理解)

33.搜索旋转排序数组 ​ 这是一个非常有趣的问题&#xff0c;如果不要求使用O(logn)应该没人会想到吧。。 方法一&#xff1a; 极致的分类讨论。旋转排序数组&#xff0c;无非就是右边的增区间的数小于左边的增区间的数&#xff0c;然后依次排序。因此我们只需要分三类讨论即可…