【C++进阶之路】模拟实现string类

news2024/11/13 23:09:24

前言

本文所属专栏——【C++进阶之路】

 上一篇,我们讲解了string类接口的基本使用,今天我们就实战从底层实现自己的string类,当然实现所有的接口难度很大,我们今天主要实现的常用的接口~

一、String类

①要点说明

  • 1.为了不与库里面的string冲突,我们需要命名空间自己实现的类进行封装
  • 2.这里我们实现的框架是按照顺序表的数据结构进行实现的。
  • 3.为了理解,下面的接口是分开讲解的,最后我会给出源码。

②私有成员

这里顺便把框架给出~

namespace my_string
{
	class string
	{
	public:
		//迭代器——begin和end要用
		typedef char* iterator;
		typedef const char* const_iterator;
		
	private:
			char* _str;
			size_t _size;
			size_t _capacity;
			static	size_t npos;
	};
	size_t string::npos = -1;
}

前3个私有成员或许都明白,那最后一个成员我们就要额外介绍一下,由于是size_t类型,vs64位平台下是8字节32位平台下是4字节,且由于是无符号的只会大于等于0,又因为是static修饰的,所以不走初始化列表初始化,由于这里只是声明,所以只能在类外进行定义,在定义时只需声明类域和变量类型即可,存储类(static)不必在写。所以就是上述的写法。有啥用呢?在接下来的insert接口中我们会用到。

③构造函数

我们这里需要跟库里面大致看齐,所以这里说一下库里参数的概念,_size和_capacity指的是有效字符,不包含字符串的最后一个\0,所以这里初始化时,会多开一个字节的空间用于存放\0,但_capacity和_size相同。

1.构造

string(const char* str = "")
{
	int len = strlen(str);
	_str = new char[len + 1];
	memcpy(_str, str,len + 1);
	_size = len;
	_capacity = len;//_capacity存的是多少个有效字符
}

2.拷贝构造

第一,这里涉及到深拷贝和浅拷贝。第二,这里涉及向谁(\0和_size)看齐的问题。我们先来解决第一个问题。

在这里插入图片描述

  • 浅拷贝,管理的是同一块空间,会析构两次十分危险,因此会直接报错。

当只进行只读时我们深拷贝出来的空间会造成一定程度上的空间消耗,但当我们浅拷贝时又会面临上面的问题。

那有没有什么办法解决这个问题呢?到还真有一个写时拷贝技术

这里我只简单的介绍一下原理,并不深入介绍,有兴趣自行了解~

先说下好处:在进行只读时我们进行的是浅拷贝,在修改时我们才进行深拷贝。

再说原理:

在这里插入图片描述

再来解决第二个问题,这就又涉及到库的实现,直接说库是咋实现的吧,举个例子:
在这里插入图片描述
也就是说库里面拷贝构造,不按照到第一个\0为止,而是最后一个,这咋解决呢?其实很简单,按照_size进行拷贝即可,用到的函数为memcpy

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

④析构函数


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

}

⑤c_str

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

后面的const有两个作用:

  • 使 *this不可被修改
  • const修饰string和string都可调用此函数

⑤size

由于_size成员是私有的,因此我们在使用时需要通过公有函数进行访问。

size_t size() const
{
	return _size;
}
  • const作用同上

⑥[]

1 .读写

  • 注意:需要检查位置是否越界。
char& operator[](size_t pos)
{
	//判断pos位置是否越界
	assert(pos < _size);
	return _str[pos];
}

1.只读

const char& operator[](size_t pos) const
{
	//判断pos位置是否越界
	assert(pos < _size);
	return _str[pos];
}

⑦reserve

  • 只有当n大于容量时我们才进行扩容。
void reserve(size_t n = 0)
{
	if (n  > _capacity)
	{
		char* tmp = new char[n+1];
		memcpy(tmp, _str, _size+1);
		delete[]_str;
		_str = tmp;
		_capacity = n;
	}
}

⑧push_back

考虑三个问题:

  • 是否需要扩容
  • 扩容时字符串是否为空
  • 尾插之后要补\0
void push_back(char c)
{
	//考虑扩容
	if (_size == _capacity)
	{
		//有可能是空字符串
		size_t new_capacity = _capacity == 0 ? 4 : _capacity * 2;
		reserve(new_capacity);
	}
	_str[_size++] = c;
	//注意这里一定要给'\0'
	_str[_size] = '\0';
}

⑨append

  • 由于是插入的是一个字符串,这里我们的扩容的条件是一个范围
void append(const char* str)
{
	int len = strlen(str);
	if (len + _size >= _capacity)
	{
		reserve(len + _size);
	}
	memcpy(_str + _size, str, len + 1);
	//strcpy会将\0拷贝过去,但是这里我们是memcpy,所以要多拷贝一个。
	_size += len;
}

⑩+=

复用上面的⑧和⑨。

1.字符串

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

2.字符

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

⑪insert

主要逻辑:
在这里插入图片描述
这里需要考虑pos(size_t)位置如果为0,由于这里是从前往后挪,我们要对下标不断做减法,而判断的条件必然是下标大于等于pos继续,当下标再一减,就会变成很大的数,所以这里要不把数据类型强转成整形,要不就设置npos用来判断是否越界,这里我们取后者。
注意:

  • 要检查pos位置的合法性
  • 看是否需要扩容

1.插入字符

string& insert(size_t pos, size_t n, char c)
{
	//看pos位置是否合法
	assert(pos < _size);
	//看是否要扩容
	reserve(n + _size);
	//将pos位置的数据进行移动
	//npos是为了防止i--越界访问,因为size_t的范围是大于等于0的,
	//当pos为0,如果不设置npos会出现死循环
	for (size_t i = _size; i >= pos&& i!= npos; i--)
	{
		_str[i + n] = _str[i] ;
	}
	//将位置插入字符c
	for (int i = pos; i < pos + n; i++)
	{
		_str[i] = c;
	}
	_size += n;
	return *this;
}

2.插入字符串

string& insert(size_t pos, const char* str)
{
	assert(pos < _size);
	int len = strlen(str);
	
	reserve(len + _size);
	for (size_t i = _size; i >= pos&&i != npos; i--)
	{
		_str[i + len] = _str[i];
	}
	//将字符串拷贝过去,当然这里\0就不用拷过去了。
	memcpy(_str + pos, str, len);
	_size += len;

	return *this;
}

⑫earse

主要逻辑:
在这里插入图片描述
注意事项:

  • pos位置的合法性
  • len和pos+len是否大于_size
string& earse(size_t pos = 0, size_t len = npos)
{
	assert(pos < _size);
	if (len == npos || pos + len >= _size )
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		for (size_t i = pos + len; i <= _size; i++)
		{
			_str[i - len] = _str[i];
		}
		_size -= len;
	}
	return *this;
}

⑬find

  • 失败返回npos

1.字符

size_t find(char c, size_t pos = 0) const
{
	assert(pos < _size);
	int begin = 0;
	while (begin < _size && _str[begin] != c)
	{
		begin++;
	}
	if (begin == _size)
	{
		return npos;
	}
	else
	{
		return begin;
	}
}

2.字符串

  • 有兴趣可自行了解KMP和BMP算法。

这里我直接用库里的strstr,失败会返回空指针。

size_t find(const char* str, size_t pos = 0)
{
	assert(pos < _size);
	char* ret = strstr(_str + pos,str);
	if (ret == nullptr)
	{
		return -1;
	}
	else
	{
		return ret - _str;
	}
}

⑭substr

注意事项:

  • pos位置的合法性
  • 字符串区间的合法性

主要逻辑:

  • 找到起始和结束区间
string substr(size_t pos = 0, size_t len = npos)
{
	assert(pos < _size);
	size_t begin = pos;
	size_t end = pos + len;
	if (len == npos|| pos + len >= _size)
	{
		end = _size;
	}
	string tmp;
	for (size_t i = begin; i < end; i++)
	{
		tmp += _str[i];
	}
	return tmp;
}

⑮resize

void resize(size_t n, char c = '\0')
{
	if (n > _size)
	{
		reserve(n);
		memset(_str + _size, c, n - _size);
	}
	_str[n] = '\0';
	_size = n;
}

⑯clear

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

⑰>

主要逻辑:

在这里插入图片描述

bool operator>(const string& s)
{
	//先比较size
	size_t less = _size > s._size ? s._size : _size;
	int  ret = memcmp(_str, s._str, less);

	if (ret == 0)
	{
		return _size > s._size;
	}
	return ret > 0;
}

⑱ ==

bool operator==(const string& s)
{
	//size_t less = _size > s.size() ? s.size() : _size;
	//int  ret = memcmp(_str, s.c_str(), less);

	//if (ret == 0)
	//{
	//	return _size == s.size();
	//}

	return _size == s._size &&
		memcmp(_str, s._str, _size);
}

至于< ,<= ,>= != 都可进行复用,这里就复用个>=。

⑲>=

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

⑳swap与 =

这两个有很大的关系,这里放到一块讲。

swap

void swap(string& str)
{
	std::swap(_str, str._str);
	std::swap(_size, str._size);
	std::swap(_capacity, str._capacity);
	//说明:不能进行对象的交换,因为swap里也有赋值,成递归调用了。
}

赋值的传统写法

string& operator=(const string& str)
{
	//传统写法
	//前提是自己不能拷贝自己
	if (this != &str)
	{
		delete[]_str;
		_str = new char[str._capacity + 1];
		memcpy(_str, str._str, _size + 1);
		_capacity = str._capacity;
		_size = str._size;
	}
	return *this;
}

较现代写法

  • 让临时变量,帮我们去析构。
string& operator=(const string& str)
{
	string tmp(str);
	swap(tmp);
	return *this;
}

现代写法

  • 直接拷贝构造临时变量。
  • 让临时变量去析构
string& operator=(string tmp)
{
	swap(tmp);
	return *this;
}

错误示例:

void swap(string& str)
{
	std::swap(*this, str);
	//说明:不能进行对象的交换,因为swap里也有赋值,成递归调用了。
}
string& operator=(string tmp)
{
	swap(tmp);
	return *this;
}

图解:
在这里插入图片描述

㉑迭代器

begin

iterator begin()
{
	return _str;
}
const_iterator begin() const
{
	return _str;
}

end

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

㉒<<

这里需要说明两点:

  • 流插入必须用引用当参数,因为拷贝库里面会强制delete进行释放,无法返回。
  • 范围for的底层原理本质上就是迭代器。

ostream& operator<<(ostream& out, const string& s)
//                                这里必须加const防止被修改
{
	for (auto c : s)
	{
		cout << c;
	}
	return out;
}

㉓>>

注意:

  • 输入即覆盖,所以我们需要对输入的string进行清空

  • 为了与库对齐,我们这里的空格和\n是跳过的,即输入: “空格”"空格"hello最后会将hello输入进去。

  • 这里我们设置了一个相当于扩大了容量,就好比你原来拿一个勺子喝水,现在要拿一个碗进行喝水,在碗满了或者没水之后,再放入string里面。

istream& operator>>(istream& in, string& s)
{

	//先清除s内部的内容 
	s.clear();
	//清除空格和\n
	char ch = in.get();
	while (ch == ' ' || ch == '\n')
	{
		ch = in.get();
	}
	//输入字符
	//由于+=要不断的进行开空间,我们可以设置一个buf将容量放大,
	//当这个buf满了,就在加上,这样节约了空间
	char buf[128];
	int i = 0;
	while (ch != ' ' && ch != '\n')
	{
		buf[i++] = ch;
		if (i == 127)
		{
			buf[i] = '\0';
			s += buf;
			i = 0;
		}
		ch = in.get();
	}
	//这里buf也可能会有数据
	if (i != 0)
	{
		buf[i] = '\0';
		s += buf;
	}
	return in;
}

源码

namespace my_string
{
	class string
	{
	public:
		//迭代器
		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin()
		{
			return _str;
		}
		const_iterator begin() const
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator end()const
		{
			return _str + _size;
		}
		//用字符串进行构造
		string(const char* str = "")
		{
			int len = strlen(str);
			_str = new char[len + 1];
			memcpy(_str, str,len + 1);
			_size = len;
			_capacity = len;//_capacity存的是多少个有效字符
		}
		//拷贝构造
		string(const string& str)
		{
			//在类域里面对象可访问其成员,不管是由谁进行调用
			_str = new char[str._size + 1];
			memcpy(_str, str._str, str._size + 1);
			_size = str._size;
			_capacity = str._capacity;

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

		}
		//返回C字符串类型
		const char* c_str() const 
		//写const的原因为——不管const或非const都可使用此成员函数
		{
			return _str;
		}
		//返回有效字符的大小
		size_t size() const//const理由同上
		{
			return _size;
		}
		//运算符重载下标引用操作符
		//返回值得引用,读和写都可行
		char& operator[](size_t pos)
		{
			//判断pos位置是否越界
			assert(pos < _size);

			return _str[pos];
		}
		//只读版本
		const char& operator[](size_t pos) const
		{
			//判断pos位置是否越界
			assert(pos < _size);

			return _str[pos];
		}
		
		//扩容,参数n是想要扩到多大
		void reserve(size_t n = 0)
		{
			if (n  > _capacity)
			{
				char* tmp = new char[n+1];
				memcpy(tmp, _str, _size+1);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}
		//尾插
		void push_back(char c)
		{
			//考虑扩容
			if (_size == _capacity)
			{
				//有可能是空字符串
				size_t new_capacity = _capacity == 0 ? \
				4 : _capacity * 2;
				reserve(new_capacity);
			}
			_str[_size++] = c;
			//注意这里一定要给'\0'
			_str[_size] = '\0';
		}
		//追加字符串
		void append(const char* str)
		{
			int len = strlen(str);
			if (len + _size >= _capacity)
			{
				reserve(len + _size);
			}
			memcpy(_str + _size, str, len + 1);

			_size += len;
		}
		//运算符重载+=
		string& operator += (const char* str)
		{
			append(str);
			return *this;
		}
		string& operator += (char c)
		{
			push_back(c);
			return *this;
		}
		string& insert(size_t pos, size_t n, char c)
		{
			//看pos位置是否合法
			assert(pos < _size);
			//看是否要扩容
			reserve(n + _size);
			//将pos位置的数据进行移动
			//npos是为了防止i--越界访问,因为size_t的范围是大于等于0的,
			//当pos为0,如果不设置npos会出现死循环
			for (size_t i = _size; i >= pos&& i!= npos; i--)
			{
				_str[i + n] = _str[i] ;
			}
			//将位置插入字符c
			for (int i = pos; i < pos + n; i++)
			{
				_str[i] = c;
			}
			_size += n;
			return *this;
		}
		string& insert(size_t pos, const char* str)
		{
			assert(pos < _size);
			int len = strlen(str);
			
			reserve(len + _size);
			for (size_t i = _size; i >= pos&&i != npos; i--)
			{
				_str[i + len] = _str[i];
			}
			//将字符串拷贝过去
			memcpy(_str + pos, str, len);
			_size += len;

			return *this;
		}
		string& earse(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);
			if (len == npos || pos + len >= _size )
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				for (size_t i = pos + len; i <= _size; i++)
				{
					_str[i - len] = _str[i];
				}
				_size -= len;
			}
			return *this;
		}
		//查找字符串的函数
		size_t find(char c, size_t pos = 0) const
		{
			assert(pos < _size);
			int begin = 0;
			while (begin < _size && _str[begin] != c)
			{
				begin++;
			}
			if (begin == _size)
			{
				return npos;
			}
			else
			{
				return begin;
			}
		}
		size_t find(const char* str, size_t pos = 0)
		{
			assert(pos < _size);
			char* ret = strstr(_str + pos,str);
			if (ret == nullptr)
			{
				return -1;
			}
			else
			{
				return ret - _str;
			}
		}
		string substr(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);
			size_t begin = pos;
			size_t end = pos + len;
			if (len == npos|| pos + len >= _size)
			{
				end = _size;
			}
			string tmp;
			for (size_t i = begin; i < end; i++)
			{
				tmp += _str[i];
			}
			return tmp;
		}
		//调整size
		//void resize(size_t n)
		//{
		//	if (n > _size)
		//	{
		//		reserve(n);
		//	}
		//	_str[n] = '\0';
		//	_size = n;
		//}
		void resize(size_t n, char c = '\0')
		{
			if (n > _size)
			{
				reserve(n);
				memset(_str + _size, c, n - _size);
			}
			_str[n] = '\0';
			_size = n;
		}
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		//比较大小
		bool operator>(const string& s)
		{
			//先比较size
			size_t less = _size > s._size ? s._size : _size;
			int  ret = memcmp(_str, s._str, less);

			if (ret == 0)
			{
				return _size > s._size;
			}

			return ret > 0;
		}
		bool operator==(const string& s)
		{
			//size_t less = _size > s.size() ? s.size() : _size;
			//int  ret = memcmp(_str, s.c_str(), less);

			//if (ret == 0)
			//{
			//	return _size == s.size();
			//}

			return _size == s._size &&
				memcmp(_str, s._str, _size);
		}
		bool operator>=(const string& s)
		{
			return *this > s || *this ==  s;
		}
		bool operator<(const string& s)
		{
			return !(*this >= s);
		}
		bool operator<=(const string& s)
		{
			return !(*this > s);
		}
		void swap(string& str)
		{
			std::swap(_str, str._str);
			std::swap(_size, str._size);
			std::swap(_capacity, str._capacity);
			//说明:不能进行对象的交换,因为swap里也有赋值,成递归调用了。
			/*std::swap(*this, str);*/

		}
		string& operator=(const string& str)
		{
			//现代写法
			//string tmp(str);
			//swap(tmp);
			//传统写法
			//前提是自己不能拷贝自己
			if (this != &str)
			{
				delete[]_str;
				_str = new char[str._capacity + 1];
				memcpy(_str, str._str, _size + 1);
				_capacity = str._capacity;
				_size = str._size;
			}
			return *this;
		}
		// 库里的swap
		
		//template <class T> void swap(T& a, T& b)
		//{
		//	T c(a); a = b; b = c;
		//}
		//最优现代写法
		string& operator=(string tmp)
		{

			swap(tmp);
			return *this;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
		static	size_t npos;
		//const static size_t npos = -1;
		//这是可行的。当做特殊语法记住即可。
	};
	size_t string::npos = -1;
	//注意流插入和流提取不能进行拷贝,因为实现是强制设置为引用,
	//拷贝会直接delete
	ostream& operator<<(ostream& out, const string& s)
	{

		//for (size_t begin = 0; begin < s.size(); begin++)
		//{
		//	out << s[begin];
		//}
		for (auto c : s)
		{
			cout << c;
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		//先清除s内部的内容 
		s.clear();
		//清除空格和\n
		char ch = in.get();
		while (ch == ' ' || ch == '\n')
		{
			ch = in.get();
		}
		//输入字符
		//由于+=要不断的进行开空间,我们可以设置一个buf将容量放大,
		//当这个buf满了,就在加上,这样节约了空间
		char buf[128];
		int i = 0;
		while (ch != ' ' && ch != '\n')
		{
			buf[i++] = ch;
			if (i == 127)
			{
				buf[i] = '\0';
				s += buf;
				i = 0;
			}
			ch = in.get();
		}
		//这里buf也可能会有数据
		if (i != 0)
		{
			buf[i] = '\0';
			s += buf;
		}
		return in;
	}
}

总结

 今天的分享就到这里了,如果觉得文章不错,点个赞鼓励一下吧!我们下篇文章再见

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

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

相关文章

※Redis的事务、乐观锁和悲观锁

1.是神魔 在高并发的环境下&#xff0c;多个线程去竞争同一个资源&#xff0c; 比较常见的有高铁抢票系统&#xff0c;商品秒杀系统等&#xff0c;我们需要保证数据正确&#xff0c;同时系统的吞吐也要尽可能高。2.解决方案 1. 一般多线程同步我们就会想到加锁&#xff0c;用…

c语言进阶-文件的打开和读写

本节重点知识点 为什么使用文件 什么是文件 文件名的组成 操作文件的基本过程 文件的打开与关闭 文件打开函数&#xff1a; 参数介绍 打开文件的方式&#xff1a; 使用绝对路径和相对路径都可以打开文件 文件的顺序读写函数&#xff1a; 写文件模式下&#xff0c;在打开文件fo…

-XX:NewSize=20m -XX:MaxNewSize=40m,-Xmn30m,-XX:NewRatio=5

高优先级&#xff1a;-XX:NewSize -XX:MaxNewSize设置新生代初始值&#xff0c;最大值中优先级&#xff1a;-Xmn&#xff08;默认等效 -Xmn-XX:NewSize-XX:MaxNewSize?&#xff09;设置新生代初始值-XX:NewRatio设置老年代和新生代的比例&#xff1b;例如&#xff1a;-XX:NewR…

CMake+OpenMP加速运算测试

目录 写在前面代码编译运行关于加速效果参考完 写在前面 1、本文内容 cmake编译测试openmp的效果 2、平台/环境 windows/linux均可&#xff0c;cmake 3、转载请注明出处&#xff1a; https://blog.csdn.net/qq_41102371/article/details/131629705 代码 代码包含同样的for循…

Dockerfile自定义镜像 - 基于 java:8-alpine 镜像,将一个Java项目构建为镜像

目录 一、前置知识 1.镜像结构 2.Dockerfile是什么 二、自定义一个 java 项目镜像 1.创建一个空目录&#xff0c;在这个空目录中创建一个文件&#xff0c;命名为 DockerFile&#xff0c;最后将 java 项目打包成 jar 包&#xff0c;放到这个目录中 2.编写 Dockerfile 文件 …

Vue3+Vite项目引入Element-plus并配置按需自动导入

一、安装Element-plus # 选择一个你喜欢的包管理器# NPM $ npm install element-plus --save# Yarn $ yarn add element-plus# pnpm $ pnpm install element-plus我使用的是 pnpm&#xff0c;并且顺便将 element-plus/icons一起引入 pnpm install element-plus element-plus/…

Python开启Http Server

用 Python 部署了一个具有 FTP 功能的服务器&#xff0c;电脑在局域网内通过 FTP 下载想要传输的文件。 注&#xff1a;这种方法不仅在自己家的路由器上可行&#xff0c;亲测在下面两种场景也可行&#xff1a; 需要用手机验证码连接的公共 WIFI 上&#xff1b;用手机开热点&a…

Kubernetes的Pod中进行容器初始化

Kubernetes的Pod中进行容器初始化 在很多应用场景中&#xff0c;应用在启动之前都需要进行如下初始化操作&#xff1a; 等待其他关联组件正确运行(例如数据库或某个后台服务)。 基于环境变量或配置模板生成配置文件。 从远程数据库获取本地所需配置&#xff0c;或者将自身注…

将一个3x3的OpenCV旋转矩阵转换为Eigen的Euler角

代码将一个3x3的OpenCV旋转矩阵转换为Eigen的Euler角。 #include <iostream> #include <Eigen/Core> #include <Eigen/Geometry> #include <opencv2/core.hpp>using

Sharding-JDBC【Sharding-JDBC介绍、数据分片剖析实战】(一)-全面详解(学习总结---从入门到深化)

目录 Sharding-JDBC介绍 数据分片剖析实战 Sharding-JDBC介绍 背景 随着通信技术的革新&#xff0c;全新领域的应用层出不穷&#xff0c;数据存量随着应 用的探索不断增加&#xff0c;数据的存储和计算模式无时无刻不面临着创新。面向交易、大数据、关联分析、物联网等场景…

初始 Redis - 分布式,内存数据存储,缓存

目录 1. 什么是 Redis 1.1 Redis 内存数据存储 1.2 Redis 用作数据库 1.3 Redis 用作缓存 (cache) 1.4 用作消息中间件 1. 什么是 Redis The open source , in-memory data store used by millions of developers as a database, cache, streaming engine, and message br…

时间序列预测 | Matlab基于自回归移动平均模型(ARMA模型)时间序列预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 时间序列预测 | Matlab基于自回归移动平均模型(ARMA模型)时间序列预测,单列数据输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 部分源码 %% 清空环境变量 w…

zookeper第二三课-Zookeeper经典应用场景实战

1. Zookeeper Java客户端实战 ZooKeeper应用的开发主要通过Java客户端API去连接和操作ZooKeeper集群。可供选择的Java客户端API有&#xff1a; ZooKeeper官方的Java客户端API。第三方的Java客户端API&#xff0c;比如Curator。 ZooKeeper官方的客户端API提供了基本的操作。例…

MFS分布式文件系统

MFS分布式文件系统 应用背景 公司之前的图片服务器采用的是 NFS&#xff0c;随着业务量增加&#xff0c;多台服务器通过 NFS方式共享一个服务器的存储空间&#xff0c;使得 NFS 服务器不堪重负&#xff0c;经常出现超时问题。 而且NFS存在着单点故障问题&#xff0c;尽管可以…

C++STL:顺序容器之vector

文章目录 1. 概述2. 成员函数3. 创建 vector 容器的几种方式4. 迭代器vector容器迭代器的基本用法vector容器迭代器的独特之处 5. 访问元素5.1 访问vector容器中单个元素5.2 访问vector容器中多个元素 6. 添加元素6.1 push_back()6.2 emplace_back()6.3 emplace_back()和push_b…

[元带你学: eMMC协议 24] eMMC Packed Command CMD23读(Read) 写(write) 操作详解

依JEDEC eMMC及经验辛苦整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《元带你学&#xff1a;eMMC协议》 内容摘要 全文 3200 字&#xff0c; 主要内容 目录 前言 1 Packed Commands 有什么用处&#xff1f; 2 Packed Commands 怎么实现&#xff1f; Packed Wri…

【爬虫】5.4 Selenium 实现用户登录

目录 任务目标 创建模拟网站 创建服务器程序 键盘输入动作 鼠标点击动作 编写爬虫程序 任务目标 Selenium 查找的 HTML 元素是一个 WebElemen t对象&#xff0c; 这个对象不但可以获取元素的属性值&#xff0c;而且还能执行一 些键盘输入send_keys()与鼠标点击click()的动…

leetcode 617. 合并二叉树

2023.7.9 这题要求合并两二叉树&#xff0c;若节点重叠则将节点值相加。 和之前不同的是需要同时对两棵树进行操作&#xff0c;我选用队列来做这题。 大致思路&#xff1a;通过遍历两棵树的对应节点&#xff0c;将节点值相加并合并到第一棵树上。如果某个节点为空&#xff0c;…

Squid 缓存服务器

Squid 缓存服务器 作为应用层的代理服务软件&#xff0c;Squid 主要提供缓存加速和应用层过滤控制的功能 ☆什么是缓存代理 当客户机通过代理来请求 Web 页面时 指定的代理服务器会先检查自己的缓存&#xff0c;如果缓存中已经有客户机需要访问的页面&#xff0c;则直接将缓…

Qt实现思维导图功能(五)

前文链接&#xff1a;Qt实现思维导图功能&#xff08;四&#xff09; 思维导图纵向分布模式&#xff1a;模式一 百度网盘体验地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1YNSBiFOUwnSSKvHsBvOT3g 提取码&#xff1a;ifyc动态演示效果 静态展示图片 前文BUG维…