C++_String增删查改模拟实现

news2024/9/22 19:40:28

C++_String增删查改模拟实现

  • 前言
  • 一、string默认构造、析构函数、拷贝构造、赋值重载
    • 1.1 默认构造
    • 1.2 析构函数
    • 1.3 拷贝构造
    • 1.4 赋值重载
  • 二、迭代器和范围for
  • 三、元素相关:operator[ ]
  • 四、容量相关:size、resize、capacity、reserve
    • 4.1 size、capacity
    • 4.2 reserve
    • 4.3 resize
  • 五、数据相关:push_bach、append、operator+=、insert、erase
    • 5.1 尾插:push_back
    • 5.2 append尾部插入字符串
    • 5.3 operator+=()字符、字符串
    • 5.4 insert插入字符、字符串
      • 5.4.1 insert插入字符(在这提醒下,博主是所有的拷贝数据都是从'\0'开始,这样就不需要单独对'\0'做处理)
        • 初学者最容易范的一个错误
      • 5.4.2 insert插入字符串
    • 5.5 erase
  • 六、 关系操作符重载:< 、 ==、 <=、 >、>=、!=
  • 七、find查找字符、字符串、substr
    • 7.1 find查找字符
    • 7.2 find查找字符串
    • 7.3 strsub( ) 模拟实现
  • 八、流插入和流提取(<<、>>)(实现在string类外)
    • 8.1 流插入<<
    • 8.1 流提取>>
      • 优化
  • 九、所有代码

前言

本篇博客仅仅实现存储字符的string。同时由于C++string库设计的不合理,博主仅实现一些最常见的增删查改接口!
接下来给出的接口都是基于以下框架:

namespace achieveString
{
	class string
	{

	private:
		char* _str;
		size_t _capacity;
		size_t _size;
	};
}

一、string默认构造、析构函数、拷贝构造、赋值重载

1.1 默认构造

博主在这仅仅提供如无参和带参默认构造接口:

//无参默认构造
string()
	:_str(new char[1]{'\0'})
	,_capacity(0)
	,_size(0)
{ }
//带参默认构造
string(const char* str = "")
	:_capacity(strlen(str))
	,_size(_capacity)
{
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

小tips:

  1. C++string标准库中,无参构造并不是空间为0,直接置为空指针。而是开一个字节,并存放‘\0’。(C++中支持无参构造一个对象后,直接在后面插入数据,也从侧面说明了这点)
  2. 由于C++构造函数不管写不写都会走初始化列表的特性,所以这里博主也走初始化列表。
  3. string中,_capacity和_size都不包含空指针,所以带参构造要多开一个空间,用来存储’\0’。

1.2 析构函数

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

1.3 拷贝构造

传统写法:

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

现代写法:现代写法的核心在于:将拷贝数据的工作交给别人来做,最后将成果交换一样即可。

//交换
void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

//现代写法
string(const string& s)
	:_str(nullptr)
	,_size(0)
	,_capacity(0)
{
	string tmp(s._str);
	swap(tmp);
}

tips:现代写法中,拷贝构造是数据需初始化为空。原因在于C++中,编译器对内置类型不会做处理(个别如vs2019等编译器会做处理的),这也就意味这_str是一个随机值,指向任意一块空间。调用析构函数时会报错。

1.4 赋值重载

赋值重载同样分为传统写法和现代写法。
传统写法:

string& operator=(const string& s)
{
	if (this != &s)
	{
		char* tmp = new char[s._capacity + 1];
		strcpy(tmp, s._str);
		delete[] _str;
		_str = tmp;
		_size = s._size;
		_capacity = s._capacity;
	}
	return *this;
}

现代写法:

//现代写法
//法一
/*string& operator=(const string& s)
{
	if (this != &s)
	{
		string tmp(s._str);
		swap(tmp);
	}
	return *this;
}*/

//法二
string& operator=(string tmp)
{
	swap(tmp);
	return *this;
}

二、迭代器和范围for

在C++中,范围for在底层是通过迭代器来实现的。所以只要实现了迭代器,就支持范围for。
而迭代器类似于指针,迭代器可以被看作是指针的一种泛化,它提供了类似指针的功能,可以进行解引用操作、指针运算等。
 
以下提供了const迭代器和非const迭代器:

typedef char* iterator;
const typedef char* const_iterator;

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

	const_iterator begin() const
	{
		return _str;
	}
	const_iterator end() const
	{
		return _str + _size;
	}

三、元素相关:operator[ ]

这里我们和库中一样,提供以下两个版本

//可读可写
char operator[](size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}
//只读
const char operator[](size_t pos)const
{
	assert(pos < _size);
	return _str[pos];
}

四、容量相关:size、resize、capacity、reserve

4.1 size、capacity

size_t size()const
{
	return _size;
}
size_t capacity()const
{
	return _capacity;
}

4.2 reserve

在C++中,我们一般不缩容。
所以实现reserve时(容量调整到n),首先判断目标容量n是否大于当前容量。如果小于就不做处理,否则先开辟n+1个内存空间(多出来的一个用于存储‘\0’),然后将原有数据拷贝到新空间(strcpy会将’\0’一并拷贝过去)。然后释放就空间,并让_str指向新空间,同时更新_capacity。

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capacity = n;
	}
}

4.3 resize

resize到目标大小分为以下3中情况:
在这里插入图片描述

  1. 当n<_size时,只需将下标为n的地址处的数据改为’\0’。
  2. 其他情况,我们直接统一处理。直接复用reserve()函数将_capacity扩到n。然后用将[_size, n)中的数据全部初始化为ch。(这里博主给ch一个初始值’\0’,但ch不一定为’\0’,所以要将下标为n处的地址初始化为’\0’)
void resize(size_t n, char ch='\0')
{
	if (n <= _size)
	{
		_str[n] = '\0';
		_size = n;
	}
	else
	{
		reserve(n);
		while (_size < n)
		{
			_str[_size] = ch;
			_size++;
		}
		_str[_size] = '\0';
	}
}

五、数据相关:push_bach、append、operator+=、insert、erase

5.1 尾插:push_back

尾插首先检查扩容,在插入数据

void push_back(char ch)
{
	//扩容
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	//插入数据
	_str[_size] = ch;
	_size++;
	_str[_size] = '\0';

}

5.2 append尾部插入字符串

void append(const char* str)
{
	size_t len = strlen(str);
	if (_size + len > _capacity)//扩容
	{
		reserve(_size + len);
	}
	strcpy(_str + _size, str);
	_size += len;
}

5.3 operator+=()字符、字符串

operator+=()字符、字符串可以直接复用push_back和append。

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

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

5.4 insert插入字符、字符串

5.4.1 insert插入字符(在这提醒下,博主是所有的拷贝数据都是从’\0’开始,这样就不需要单独对’\0’做处理)

insert插入字符逻辑上还是很简单的。
首先判断插入字符时是否需要扩容。然后从下标为pos开始,所有数据依次往后挪动。最后将待插入字符给到pos处即可

初学者最容易范的一个错误

但对于初学者来说,貌似也不太轻松。。。。。。
下面给出各位初学者容易犯的错误:

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

这样对吗?答案是错误的。

假设是在头插字符,end理论上和pos(即0)比较完后就减到-1,在下一次循环条件比较时失败,退出循环。
遗憾的是end是size_t类型,始终>=0, 会导致死循环。

博主在这给出两种解决方法:

  1. 将pos强转为整型。
void insert(size_t pos, char ch)
{
	assert(pos <= _size);
	//扩容
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	//挪动数据
	int end = _size;
	while (end >= (int)pos)
	{
		_str[end+1] = _str[end];
		end--;
	}
	_str[pos] = ch;
	_size++
}

2.从end从最后数据的后一位开始,每次将前一个数据移到当前位置。最后条件判断就转化为end>pos,不会出现死循环这种情况。
在这里插入图片描述

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

5.4.2 insert插入字符串

insert同样存在相同问题,并且思路一样。博主就直接给出代码了。
法一:

void insert(size_t pos, const char* str)
{
	int len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}

	int end = _size;
	while (end >= (int)pos)
	{
		_str[end + len] = _str[end];
		end--;
	}
	strncpy(_str + pos, str, len);
	_size += len;
}

法二:

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

5.5 erase

erase分两种情况:

  1. 从pos开始,要删的数据个数超过的字符串,即将pos后序所有数据全部情况。(直接将pos处数据置为’\0’即可)
  2. 从pos开始,要删的数据个数没有超出的字符串。所以只需要从pos+len位置后的所有数据向前移动从pos位置覆盖原数据即可。
void erase(size_t pos, size_t len = npos)
{
	if (len==npos || pos + len >= _size)
	{
		//有多少,删多少
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		size_t begin = pos + len;
		while (begin <= _size)
		{
			_str[begin - len] = _str[begin];
			begin++;
		}
		_size -= len;
	}
}

六、 关系操作符重载:< 、 ==、 <=、 >、>=、!=

bool operator<(const string& s) const
{
	return strcmp(_str, s._str) < 0;
}

bool operator==(const string& s) const
{
	return strcmp(_str, s._str) == 0;
}

bool operator<=(const string& s) const
{
	return *this < s || *this == s;
}

bool operator>(const string& s) const
{
	return !(*this <= s);
}

bool operator>=(const string& s) const
{
	return !(*this < s);
}

bool operator!=(const string& s) const
{
	return !(*this == s);
}

七、find查找字符、字符串、substr

7.1 find查找字符

size_t find(char ch, size_t pos = 0)
{
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}

	return npos;
}

7.2 find查找字符串

size_t find(const char* sub, size_t pos = 0)
{
	const char* p = strstr(_str + pos, sub);
	if (p)
	{
		return p - _str;
	}
	else
	{
		return npos;
	}
}

7.3 strsub( ) 模拟实现

strsub目标长度可能越界string,也可能还有没有。但不管是那种情况,最后都需要拷贝数据。所以这里我们可以先将len真实长度计算出来,在拷贝数据。

string substr(size_t pos, size_t len = npos)const
{
	string s;
	size_t end = pos + len;
	//目标字符越界string,更新len
	if (len == npos || end >= _size)
	{
		len = _size - pos;
		end = _size;
	}
	
	//拷贝数据
	s.reserve(len);
	for (size_t i = pos; i < end; i++)
	{
		s += _str[i];
	}

	return s;
}

八、流插入和流提取(<<、>>)(实现在string类外)

8.1 流插入<<

由于前面我们实现了迭代器,所以最简单的方式就是范围for

ostream& operator<<(ostream& out, const string& s)
{
	/*for (size_t i = 0; i < s.size(); i++)
	{
		out << s[i];
	}*/
	for (auto ch : s)
		out << ch;

	return out;
}

8.1 流提取>>

流提取比较特殊。在流提取前需要将原有数据全部清空。同时由于>>无法获取空字符和换行符()(都是作为多个值之间的间隔),直接流提取到ostream对象中,没法结束。(类似于C语言中scanf, 换行符和空字符仅仅只是起到判断结束的作用,但scanf无法获取到它们)
所以这里博主直接调用istream对象中的get()函数。(类似于C语言中的getchar()函数)
get详细文档
在这里插入图片描述

class string
{
	void clear()
	{
		_str[0] = '\0';
		_size = 0;
	}
private:
	char* _str;
	size_t _capacity;
	size_t _size;
};

istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char ch;
		//in >> ch;
		ch = in.get();

		while (ch != ' ' && ch != '\n')
		{
			s += ch;
			//in >> ch;
			ch = in.get();
		}
		return in;
	}

上面这种方法虽然可以达到目的。但还有一个问题,每次插入数据都面临可扩容问题。那如何优化呢?

优化

其中一种办法就是调用reserve()提前开好空间,但这样面临这另一个问题:开大了浪费空间;开小了,同样面临这扩容的问题。
所以在这博主采用和vs底层实现的思路:首先开好一段数组(包含’\0’,以16为例)。当数据个数小于16时,字符串存在数组中;当数据个数大于等于16时,将数据存在_str指向的空间。
这是一种以空间换时间的思路,同时也能很好的减少内存碎片的问题。

class string
{
	void clear()
	{
		_str[0] = '\0';
		_size = 0;
	}
private:
	char* _str;
	size_t _capacity;
	size_t _size;
};

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

	char buff[16];
	size_t i = 0;

	char ch;
	ch = in.get();
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		if (i == 16)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}
	ch = in.get();
	}

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

九、所有代码

namespace achieveString
{
	class string
	{
	public:
	typedef char* iterator;
	const typedef char* const_iterator;

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

	const_iterator begin() const
	{
		return _str;
	}
	const_iterator end() const
	{
		return _str + _size;
	}

		//构造函数
		/*string()
			:_str(new char[1]{'\0'})
			,_capacity(0)
			,_size(0)
		{ }*/
		string(const char* str = "")
			:_capacity(strlen(str))
			, _size(_capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		const char* c_str() const
		{
			return _str;
		}

		//析构函数
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		//拷贝构造
		/*string(const string& s)
		{
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}*/

		//交换
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		//现代写法
		string(const string& s)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
		}

		// 赋值重载
		/*string& operator=(const string& s)
		{
			if (this != &s)
			{
				char* tmp = new char[s._capacity + 1];
				strcpy(tmp, s._str);
				delete[] _str;
				_str = tmp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}*/
		//现代写法
		//法一
		/*string& operator=(const string& s)
		{
			if (this != &s)
			{
				string tmp(s._str);
				swap(tmp);
			}
			return *this;
		}*/
		//法二
		string& operator=(string tmp)
		{
			swap(tmp);
			return *this;
		}

		//可读可写
		char operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		//只读
		const char operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}

		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _capacity;
		}

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

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}

		void resize(size_t n, char ch='\0')
		{
			if (n <= _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				while (_size < n)
				{
					_str[_size] = ch;
					_size++;
				}
				_str[_size] = '\0';
			}
		}

		void push_back(char ch)
		{
			//扩容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//插入数据
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
		
		}

		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
		}

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

		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
		
		void insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			//扩容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//挪动数据
			size_t end = _size+1;
			while (end > pos)
			{
				_str[end] = _str[end-1];
				end--;
			}
			//插入数据,更新_size
			_str[pos] = ch;
			_size++;
		}
		void insert(size_t pos, const char* str)
		{
			int len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			//法一
			/*int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];
				end--;
			}
			strncpy(_str + pos, str, len);
			_size += len;*/

			//法二
			size_t end = _size+1;
			while (end > pos)
			{
				_str[end + len-1] = _str[end-1];
				end--;
			}
			strncpy(_str + pos, str, len);
			_size += len;
		}

		void erase(size_t pos, size_t len = npos)
		{
			if (len==npos || pos + len >= _size)
			{
				//有多少,删多少
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t begin = pos + len;
				while (begin <= _size)
				{
					_str[begin - len] = _str[begin];
					begin++;
				}
				_size -= len;
			}
		}
		bool operator<(const string& s)const
		{
			return strcmp(_str, s._str) < 0;
		}

		bool operator==(const string& s)const
		{
			return strcmp(_str, s._str) == 0;
		}

		bool operator<=(const string& s)const
		{
			return *this == s && *this < s;
		}

		bool operator>(const string& s)const
		{
			return !(*this <= s);
		}

		bool operator>=(const string& s)const
		{
			return !(*this < s);
		}

		bool operator!=(const string& s)const
		{
			return !(*this == s);
		}

		size_t find(char ch, size_t pos = 0)
		{
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
					return i;
			}
			return npos;
		}

		size_t find(const char* sub, size_t pos = 0)
		{
			const char* p = strstr(_str + pos, sub);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos, size_t len = npos)
		{
			string s;
			size_t end = pos + len;
			if (len == npos || end >= _size)
			{
				len = _size - pos;
				end = _size;
			}
			
			s.reserve(len);
			for (size_t i = pos; i < end; i++)
			{
				s += _str[i];
			}

			return s;
		}
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
	private:
		char* _str;
		size_t _capacity;
		size_t _size;

		//const static size_t npos = -1;  // C++支持const整型静态变量在声明时给值初始化,但不建议
		//const static double npos = 1.1;  // 不支持

		const static size_t npos;
	};
	const size_t string::npos = -1;

	ostream& operator<<(ostream& out, const string& s)
	{
		/*for (size_t i = 0; i < s.size(); i++)
		{
			out << s[i];
		}*/
		for (auto ch : s)
			out << ch;

		return out;
	}

	//istream& operator>>(istream& in, string& s)
	//{
	//	s.clear();
	//	char ch;
	//	//in >> ch;
	//	ch = in.get();

	//	while (ch != ' ' && ch != '\n')
	//	{
	//		s += ch;
	//		//in >> ch;
	//		ch = in.get();
	//	}
	//	return in;
	//}

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

		char buff[16];
		size_t i = 0;

		char ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 16)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}

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

		return in;
	}
}

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

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

相关文章

【Redis篇】简述Redis | 详解Redis命令

文章目录 &#x1f38d;什么是Redis&#x1f38d;Redis特点&#x1f38d;Redis应用场景&#x1f354;Windows安装Redis⭐启动Redis &#x1f33a;Redis数据类型&#x1f33a;Redis常用命令⭐字符串string操作命令⭐哈希hash操作命令⭐列表list操作命令⭐集合set操作命令⭐有序集…

在Ubuntu18.04安装适合jdk8的eclipse

直接在Ubuntu软件那里下载的eclipse不能用&#xff0c;下载后启动会报错&#xff1a;Eclipse An error has occurred. See the log file/home/hadoop/.eclipse/ org.eclipse.platform_3.8_155965261/ configuration/1700567835954.log 上网搜索方法&#xff0c;按教程说的修改e…

华为认证 | HCIE考证流程详解!

HCIE&#xff08;Huawei Certified ICT Expert&#xff0c;华为认证ICT专家&#xff09;是华为认证体系中最高级别的ICT技术认证&#xff0c;旨在打造高含金量的专家级认证&#xff0c;为技术融合背景下的ICT产业提供新的能力标准&#xff0c;以实现华为认证引领ICT行业技术认证…

斯坦福NLP课程来了

生成式AI&#xff0c;尤其是以ChatGPT为首的大语言模型正在改变人们的生活方式&#xff0c;我想一定有小伙伴想加入NLP这个行列。 微软重磅发布4个适合初学者的机器学习资料 我在前一篇文章中分享了微软人工智能初学者课程&#xff0c;其中的【生成式AI】非常适合初学者&…

系列六、ThreadLocal内存泄漏案例

一、内存泄漏 vs 内存溢出 内存泄漏&#xff1a;内存泄漏是指程序中已经动态分配的堆内存由于某种原因程序未释放或者无法释放&#xff0c;造成系统内存的浪费&#xff0c;导致程序运行速度减慢甚至导致系统崩溃等严重后果&#xff0c;内存泄漏最终 会导致内…

2023年中国高压驱动芯片分类、市场规模及发展趋势分析[图]

高压驱动芯片是一种能在高压环境下工作的集成电路&#xff0c;主要用于控制和驱动各种功率器件&#xff0c;如继电器、电磁阀、电机、变频器等。高压驱动芯片根据其输出电流的大小和形式可分为两类恒流型和开关型。 高压驱动芯片分类 资料来源&#xff1a;共研产业咨询&#x…

CorelDRAW2024有哪些新功能?如何下载

作为一个广告设计者怎么能不会一两款专业设计软件&#xff0c;Adobe公司的PS、AI和Corel公司的CorelDRAW都是常用的图片处理软件&#xff0c;也是市场上同类设计软件中的翘楚。针对不同的设计任务选择不同的设计软件&#xff0c;才能提高工作效率&#xff0c;事半功倍。 Corel…

七个步骤 从零到servlet第一个hello

目录 Tomcat安装 Servlet 1.引入项目 2.引入依赖 3.创建目录 1.创建 webapp 目录 2.创建 web.xml 3.编写 web.xml 4.编写代码 5.打包程序 6.部署程序 7.验证程序 Smart Tomcat Tomcat安装 在动漫世界&#xff0c;我们有汤姆猫 在Java世界&#xff0c;同样也有一个…

精彩预告 | OpenHarmony即将亮相MTSC 2023

MTSC 2023 第12届中国互联网测试开发大会&#xff08;深圳站&#xff09;即将于2023年11月25日&#xff0c;在深圳登喜路国际大酒店举办&#xff0c;大会将以“1个主会场4个平行分会场”的形式呈现&#xff0c;聚集一众顶尖技术专家和行业领袖&#xff0c;围绕如今备受关注的行…

redis之主从复制和哨兵模式

&#xff08;一&#xff09;redis的性能管理 1、redis的数据缓存在内存中 2、查看redis的性能&#xff1a;info memory&#xff08;重点&#xff09; used_memory:904192&#xff08;单位字节&#xff09; redis中数据占用的内存 used_memory_rss:10522624 redis向操作系统…

锯木棍

题目描述 有一根粗细均匀长度为 L 的木棍&#xff0c;先用红颜色刻度线将它 m 等分&#xff0c;再用蓝色刻度线将 其 n 等分&#xff08; m>n &#xff09;&#xff0c;然后按所有刻度线将该木棍锯成小段&#xff0c;计算并输出长度最长的木棍的长度和根数。 输入格式…

(内部资料)收下这几个人工智能学习秘籍!

秘籍一&#xff1a;练好基本功 学习基础知识&#xff1a;人工智能涉及多个学科领域&#xff0c;包括数学、计算机科学、统计学等。因此&#xff0c;学习基础知识是非常重要的。您可以通过学习线性代数、概率论和微积分等数学基础知识&#xff0c;以及掌握Python编程语言和常用…

Linux学习笔记-Ubuntu下使用Crontab设置定时任务

文章目录 一、概述二、基于crontab的设置2.1 基本命令说明2.2 使用-e指令编辑命令2.2.1 进入编辑模式2.2.2 指令信息格式2.2.4 开启日志1) 修改rsyslog配置文件2) 重启rsyslog3) 查看日志 2.2.3 设置后之后重启服务 三、示例3.1 每隔一分钟往文件中日期3.2 使用-l查看任务列表3…

微软重磅发布4个适合初学者的机器学习资料

自媒体火起来后&#xff0c;很多科技大佬都开始写博客&#xff0c;录视频了&#xff0c;大佬一入行&#xff0c;整个行业卷上天&#xff0c;像我这样的也只能走资源整合之路了&#xff0c;不过这样也好&#xff0c;科技进步&#xff0c;人类发展需要他们。 除了个人&#xff0…

深度学习常见激活函数:ReLU,sigmoid,Tanh,softmax,Leaky ReLU,PReLU,ELU整理集合,应用场景

文章目录 1、ReLU 函数&#xff08;隐藏层中是一个常用的默认选择&#xff09;1.1 优点1.2 缺点 2、sigmoid 函数2.1 优点2.2 缺点 3、Tanh 函数3.1 优点3.2 缺点 4、softmax 函数&#xff08;多分类任务最后一层都会使用&#xff09;5、Leaky ReLU 函数5.1 优点5.2 缺点 6、PR…

KDE 项目发布了 KDE Gear 23.08.3

导读KDE 项目发布了 KDE Gear 23.08.3&#xff0c;作为最新的 KDE Gear 23.08 开源集合的第三次维护更新&#xff0c;该集合包含了用于 KDE Plasma 桌面环境和其他平台的 KDE 应用程序。 KDE Gear 23.08.3 是在 KDE Gear 23.08.2 大约一个月之后发布的&#xff0c;包含了更多对…

Maven中常用命令以及idea中使用maven指南

文章目录 Maven 常用命令compiletestcleanpackageinstallMaven 指令的生命周期maven 的概念模型 idea 开发maven 项目idea 的maven 配置idea 中创建一个maven 的web 工程在pom.xml 文件添加坐标坐标的来源方式依赖范围编写servlet maven 工程运行调试 Maven 常用命令 compile …

阿里云高效计划学生和老师免费代金券申请认证方法

阿里云高校计划学生和教师均可参与&#xff0c;完成学生认证和教师验证后学生可以免费领取300元无门槛代金券和3折优惠折扣&#xff0c;适用于云服务器等全量公共云产品&#xff0c;订单原价金额封顶5000元/年&#xff0c;阿里云百科aliyunbaike.com分享阿里云高校计划入口及学…

掌握技术访谈:CNN、Seq2Seq、Faster R-CNN 和 PPO — 揭开卓越编码和创新之路

一、说明 本文 揭开CNN、Seq2Seq、Faster R-CNN 和 PPO — 编码和创新之路。对于此类编程的短小示例&#xff0c;用于开发时临时参考。 二、数据准备 问题陈述&#xff1a;在本次挑战中&#xff0c;您将深入计算机视觉世界并使用卷积神经网络 (CNN) 解决图像分类任务。您将使用…

Figma最全面的新手指南,从基础到高级,一网打尽

1 Figma界面介绍 Figma基础界面与传统设计软件没有太大区别&#xff0c;有Sketch使用经验的用户几乎可以无缝连接到Figma。 立即体验 免费的在线Figma汉化版即时设计是一款支持在线协作的专业级 UI 设计工具&#xff0c;支持 Sketch、Figma、XD 格式导入&#xff0c;海量优质设…