【C++】——string的模拟实现

news2025/1/23 3:27:28

前言:

在之前的学习中,我们已经对string类进行了简单的介绍,大家只要能够正常使用即可。但是在面试中,面试官总喜欢让学生自己 来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。因此,接下来我将带领大家手动模拟实现一下。


目录

(一)成员函数

1、构造函数

2、拷贝构造

3、赋值重载

4、析构函数

(二)容量

1、size()

2、capacity()

3、reserve()

4、resize()

5、clear()

(三)元素访问

1、 operator[]

(四)修改 

1、 operator+=

2、append()

3、push_back()

4、insert()

5、erase()

6、swap()

(五)字符串操作 

 1、c_str()

2、find()

(六)非成员函数重载

1、relational operators()

2、operator<<

3、operator>>

(七)代码汇总

(八)总结


(一)成员函数

1、构造函数

刚开始时,如果我们要实现构造函数,可能就需要分别实现带参的构造函数和无参的构造函数,但是有没有简单方法可以做到一步到位呢?

 💨  因此,为了更加的灵活方便,我们直接把带参的构造函数和无参构造函数集合,形成全缺省的构造函数,这样就省得再去写两个构造函数。

代码如下:

//全缺省的构造函数
//string(const char* str = nullptr)  //不可以,对其解引用如果遇到空指针就报错
//string(const char* str = '\0')      //类型不匹配,char 不能匹配为指针
//string(const char* str = "\0")      //可以
string(const char* str = "") 
	:_size(strlen(str))
{
	_capacity = _size == 0 ? 5 : _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

2、拷贝构造

编译器默认的实现的是浅拷贝,但是浅拷贝存在问题:

  • 如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

因此为了解决上述的问题,可以采用深拷贝解决浅拷贝问题:

  • 每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。

代码如下:

//深拷贝
// str3(str2)
string(const string& STR)
	:_size(STR._size)
	, _capacity(STR._capacity)
{
	_str = new char[STR._capacity + 1];
	strcpy(_str, STR._str);
}

3、赋值重载

注意:

  1. 当以拷贝的方式初始化一个对象时,会调用拷贝构造函数;
  2. 当给一个对象赋值时,会调用重载过的赋值运算符。

即使我们没有显式的重载赋值运算符,编译器也会以默认地方式重载它。默认重载的赋值运算符功能很简单,就是将原有对象的所有成员变量一一赋值给新对象,这和默认拷贝构造函数的功能类似。

代码如下:

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

		_size = STR._size;
		_capacity = STR._capacity;
	}
	return *this;
}

4、析构函数

析构函数的实现就比较简单,只需将指针所指的空间进行释放并把置空即可(防止野指针)  ,最后把剩余的两个成员置为0即可。

代码如下:

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

(二)容量

1、size()

顾名思义返回字符串的长度(以字符数为单位)

代码如下:

size_t size() const
{
	return _size;
}

2、capacity()

返回当前为basic_string分配的存储空间的大小,以字符表示。

代码如下:

size_t capacity() const
{
	return _capacity;
}

3、reserve()

表示请求更改容量,使字符串容量适应计划的大小更改为最多 n 个字符。

注意是有效字符,不包含标识字符,而在具体实现的时候,我们在底层多开一个空间给\0。

代码如下:

//扩容操作
void reserve(size_t N)
{
	if (N > _capacity)
	{
		char* tmp = new char[N + 1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;

		_capacity = N;
	}
}

4、resize()

其实它的情况大体上可以分为插入数据和删除数据两种情况。

  • 1.对于插入数据来说直接调用【reserve】提前预留好空间,然后搞一个for循环将字符ch尾插到数组里面去,最后再在数组末尾插入一个\0标识字符;
  • 2.对于删除数据就比较简单了,如果 n 小于当前字符串长度,则当前值将缩短为其第一个 n 个字符,删除第 n 个字符以外的字符然后重置一下_size的大小为n即可。

代码如下:


		//扩容+初始化
		void resize(size_t n, char STR = '\0')
		{
			if (n < _size)
			{
				// 删除数据--保留前n个
				_size = n;
				_str[_size] = '\0';
			}
			else if (n > _size)
			{
				if (n > _capacity)
				{
					reserve(n);
				}

				size_t end = _size;
				while (end < n)
				{
					_str[end] = STR;
					end++;
				}

				_size = n;
				_str[_size] = '\0';
			}
		}

5、clear()

顾名思义就是清除字符串,擦除basic_string的内容,该内容变为空字符串(长度为 0 个字符)。

代码如下:

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

(三)元素访问

1、 operator[]

元素访问操作相对来说用的最多的就是operator[] ;

  1. 对它进行调用时可能进行的是写操作,也可能进行读操作,所以为了适应const和非const对象,operator[]应该实现两个版本的函数;
  2. 并且这个函数处理越界访问的态度就是assert直接断言,而at对于越界访问的态度是抛异常。

代码如下:

const char& operator[](size_t pos) const
{
	assert(pos < _size);
	return _str[pos];
}

char& operator[](size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}

 


(四)修改 

1、 operator+=

追加到字符串,通过在当前值的末尾附加其他字符来扩展

在这里我们只实现添加字符和字符串的操作;

我们可以直接复用【push_back】的操作来实现。

代码如下:

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

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

 

2、append()

追加到字符串通过在当前值的末尾附加其他字符来扩展。
  1. 我们可以直接调用strcpy接口来进行字符串的尾插,但是需要注意一点,那就是【string】类的字符串函数是不会进行自动扩容的,所以我们需要判断一下是否需要进行扩容,在空间预留好的情况下进行字符串的尾插即可实现;
  2. 其次,如果已经实现了【insert】函数的情况下。我们可以直接复用【insert】函数也可实现对应的操作。
 

代码如下:

//追加字符串
void append(const char* STR)
{
	size_t len = strlen(STR);
	if (len + _size > _capacity)
	{
		reserve(_size + len);
	}
	strcpy(_str + _size, STR);
	_size += len;

	//insert(_size, STR);
}

3、push_back()

注意:

  1. 首先对于【push_back】有一个特别需要注意的地方就是当容量不够时的扩容操作。如果是一个空对象进行push_back的话,这时如果我们采取的二倍扩容就有问题,因为0*2还是0,所以对于空对象的情况我们应该给他一个初始的capacity值,所以上述构造函数的时候我给成了【5】,其他情况下进行二倍扩容即可;
  2. 其次,就是在尾插字符之后,要记得进行补【\0】操作,,否则在打印的时候就会有麻烦了。
  3. 最后跟【append】一样,如果已经实现了【insert】函数的情况下。我们可以直接复用【insert】函数也可实现对应的操作。

代码如下:

//尾插操作
void push_back(char STR)
{
	if (_size + 1 > _capacity)
	{
		reserve(_capacity * 2);
	}

	_str[_size] = STR;
	++_size;

	_str[_size] = '\0';

	//insert(_size, STR);
}

4、insert()

插入到字符串中,在 pos(或 p)指示的字符之前将其他字符插入

注意:

  1. 对于【insert】函数,有经常会引出错误的地方,那就是对于while循环里面的操作;
  2. 可能很多的小伙伴在while循环里面都是这样写的:_str[end + 1] = _str[end] ,那么这样写有没有问题呢?答案是会出问题的;
  3. 我们的end是size_t定义的,因为size_t是无符号数,那么-1会被认为是无符号整数,进行隐式类型转换,由于-1的补码是全1,此时就是恒大于0,程序会陷入死循环。所以我们可以不用size_t来定义end,防止发生隐式类型转换;
  4. 那么是不是只要把【size_t end = _size + len;】中的【end】用 int 定义就可以解决了呢?答案当然不是的 (是不是觉得很坑了呀!!!);
  5. 因为-1在和size_t定义的pos进行比较时,又会发生隐式类型转换。这是因为比较运算符也是运算符,只要进行运算就有可能出现隐式类型转换,因此此时又可能出现上述那样的情况,-1就又会被转为无符号整型,程序就又陷入死循环;
  6. 那么有没有解决方法呢?当然是有的,我们只需在比较时将【size_t】的pos强转为【int】类型,此时再去比较就没得问题了;
  7. 但当我们就想使用size_t类型,通过把【end-1】位置的元素挪到【end】位置上去,在while循环条件的判断位置,我们用end来和pos位置进行比较,end应该大于pos的位置,一旦end=pos我们就跳出循环,这样就可以了。

代码如下:

       //插入字符操作
		string& insert(size_t pos, char STR_1)
		{
			assert(pos < _size);
			if (_size + 1 > _capacity)
			{
				reserve(2 * _capacity);
			}

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

			_str[pos] = STR_1;
			++_size;

			return *this;
		}


		//插入字符串
		string& insert(size_t pos, const char* STR_2)
		{
			assert(pos < _size);
			size_t len = strlen(STR_2);
			if (_size + len > _capacity){
				reserve(_size + len);
			}

			// 挪动数据
			size_t end = _size + len;
			while (end > pos + len - 1)
			{
				_str[end] = _str[end - len];
				--end;
			}

			// 拷贝插入
			strncpy(_str + pos, STR_2, len);
			_size += len;

			return *this;
		}

5、erase()

意思很简单,就是从字符串中删除字符

对于删除,思路很简单,分为两种情况下的删除:

  • 1.如果当前位置加上要删除的长度大于字符串的长度,即【 pos + len >= _size】,此时的意思即为删除pos之后的所有元素;
  • 2.除了上述情况,就是在字符串内正常删除操作。我们只需利用strcpy来进行,将pos+len之后的字符串直接覆盖到pos位置,这样实际上就完成了删除的工作。

注意:

  1. 对于【npos】这个参数,首先我们知道对于静态成员变量,它的规则是在类外定义,类里面声明,定义时不加static关键字
  2. 但如果静态成员变量有const修饰,这时它可以在类内直接进行定义,这样的特性只针对于整型,对于其他类型则是不适用的;
  3. npos就是const static修饰的成员变量,可以直接在类内进行定义。

代码如下:


		//删除操作
		string& erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}

			return *this;
		}

6、swap()

至于交换,这个就没有必要再多说什么了很简单,我相信大家肯定也会这个。

代码如下:

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

 


(五)字符串操作 

 1、c_str()

获取等效的 C 字符串,返回指向一个数组的指针,该数组包含以 null 结尾的字符序列(即 C 字符串),表示basic_string对象的当前值
代码展示:
        const char* c_str()
		{
			return _str;
		}

2、find()

查找字符串中的第一个匹配项在basic_string中搜索由其参数指定的序列的第一个匹配项。
对于这个函数不用多说,就是对其进行遍历查找即可。

代码展示:

        //查找
		size_t find(char STR, size_t pos = 0)
		{
			assert(pos < _size);

			for (size_t i = pos; i < _size; ++i)
			{
				if (_str[i] == STR)
				{
					return i;
				}
			}

			return npos;
		}

		size_t find(const char* STR, size_t pos = 0)
		{
			assert(pos < _size);

			char* p = strstr(_str + pos, STR);
			if (p == nullptr)
			{
				return npos;
			}
			else
			{
				return p - _str;
			}
		}

 


(六)非成员函数重载

1、relational operators()

basic_string的关系运算符,以ascll码的方式比较大小

这个实现的过程,跟之前日期类的时间如出一辙,基本上都是一样的。

代码如下:

       //比较大小
		bool operator >(const string& STR) const
		{
			return strcmp(_str, STR._str) > 0;
		}

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

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

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

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

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

2、operator<<

将字符串插入流将符合 str 值的字符序列插入到 os 中。

代码如下:

	//operator<<
	ostream& operator<<(ostream& out, const string& STR)
	{
		for (auto e : STR)
		{
			out << e;
		}
		return out;
	}

3、operator>>

从流中提取字符串输入流中提取字符串,将序列存储在 str 中,该序列被覆盖(替换 str 的先前值)

注意:

  • 流提取是以空格和\n作为间隔标志的 ,而【getline】则是以【\0】就停止。

 代码如下:

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

		char ch = in.get();
        //如果输入到缓冲区里的字符串非常非常的长,那么+=就需要频繁的扩容,则效率就会降低
        //因此,在这里可以使用开辟一个数组,先将有效数据放入数组中,在进行操作,可有效提高效率
		char buff[128];   
		size_t i = 0;

		while (ch != ' ' && ch != '\0')
		{
			buff[i++] = ch;
			if (i == 127)      //最后得留一个位置给\0
			{
				buff[127] = '\0';
				STR += buff;
				i = 0;
			}

			ch = in.get();
		}
		if (i != 0)
		{
			buff[i] = '\0';
			STR += buff;
		}

		return in;

	}

 

(七)代码汇总

代码汇总如下:

        typedef char* iterator;
		typedef const 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(const char* str = nullptr)  //不可以,对其解引用如果遇到空指针就报错
		//string(const char* str = '\0')      //类型不匹配,char 不能匹配为指针
		//string(const char* str = "\0")      //可以
		string(const char* str = "") 
			:_size(strlen(str))
		{
			_capacity = _size == 0 ? 5 : _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		//深拷贝
		// str3(str2)
		string(const string& STR)
			:_size(STR._size)
			, _capacity(STR._capacity)
		{
			_str = new char[STR._capacity + 1];
			strcpy(_str, STR._str);
		}


	    //赋值操作	
		string& operator=(const string& STR)
		{
			if (this != &STR)
			{
				// str1 = str1 的情况不满足
				/*delete[] _str;
				_str = new char[s._capaicty + 1];
				strcpy(_str, s._str);
				_size = s._size;
				_capaicty = s._capaicty;*/

				char* tmp = new char[STR._capacity + 1];
				strcpy(tmp, STR._str);
				delete[] _str;
				_str = tmp;

				_size = STR._size;
				_capacity = STR._capacity;
			}
			return *this;
		}


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


		const char* c_str()
		{
			return _str;
		}

		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		size_t size() const
		{
			return _size;
		}

		size_t capacity() const
		{
			return _capacity;
		}
		//比较大小
		bool operator >(const string& STR) const
		{
			return strcmp(_str, STR._str) > 0;
		}

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

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

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

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

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

		//扩容+初始化
		void resize(size_t n, char STR = '\0')
		{
			if (n < _size)
			{
				// 删除数据--保留前n个
				_size = n;
				_str[_size] = '\0';
			}
			else if (n > _size)
			{
				if (n > _capacity)
				{
					reserve(n);
				}

				size_t end = _size;
				while (end < n)
				{
					_str[end] = STR;
					end++;
				}

				_size = n;
				_str[_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 push_back(char STR)
		{
			if (_size + 1 > _capacity)
			{
				reserve(_capacity * 2);
			}

			_str[_size] = STR;
			++_size;

			_str[_size] = '\0';

			//insert(_size, STR);
		}


		//追加字符串
		void append(const char* STR)
		{
			size_t len = strlen(STR);
			if (len + _size > _capacity)
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, STR);
			_size += len;

			//insert(_size, STR);
		}


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

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

		//插入字符操作
		string& insert(size_t pos, char STR_1)
		{
			assert(pos < _size);
			if (_size + 1 > _capacity)
			{
				reserve(2 * _capacity);
			}

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

			_str[pos] = STR_1;
			++_size;

			return *this;
		}


		//插入字符串
		string& insert(size_t pos, const char* STR_2)
		{
			assert(pos < _size);
			size_t len = strlen(STR_2);
			if (_size + len > _capacity){
				reserve(_size + len);
			}

			// 挪动数据
			size_t end = _size + len;
			while (end > pos + len - 1)
			{
				_str[end] = _str[end - len];
				--end;
			}

			// 拷贝插入
			strncpy(_str + pos, STR_2, len);
			_size += len;

			return *this;
		}


		//删除操作
		string& erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}

			return *this;
		}


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


		//查找
		size_t find(char STR, size_t pos = 0)
		{
			assert(pos < _size);

			for (size_t i = pos; i < _size; ++i)
			{
				if (_str[i] == STR)
				{
					return i;
				}
			}

			return npos;
		}

		size_t find(const char* STR, size_t pos = 0)
		{
			assert(pos < _size);

			char* p = strstr(_str + pos, STR);
			if (p == nullptr)
			{
				return npos;
			}
			else
			{
				return p - _str;
			}
		}

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

    //operator<<
	ostream& operator<<(ostream& out, const string& STR)
	{
		for (auto e : STR)
		{
			out << e;
		}
		return out;
	}

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

		char ch = in.get();
		char buff[128];
		size_t i = 0;

		while (ch != ' ' && ch != '\0')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				STR += buff;
				i = 0;
			}

			ch = in.get();
		}
		if (i != 0)
		{
			buff[i] = '\0';
			STR += buff;
		}

		return in;

	}


(八)总结

到此,关于string的模拟实现,在这里我们主要实现的是经常用得到的,对于其他的,我们并没有一一列举。如果后面有机会再给大家展示。

接下来,我们简单总结一下本文:

  1. 我们从文档的先后顺序入手,依次对各个板块的常用接口进行了模拟实现;
  2. 大家在上手操作的时候,一定要想明白为什么,做到真正的掌握string类它是非常重要的。在面试中,面试官总喜欢让学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数

到此,便于string类的模拟实现便讲解完毕了。希望本文对大家有所帮助,感谢各位的观看!!!

 

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

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

相关文章

lightroom磨皮滤镜中文插件Portraiture4最新版本

哈喽&#xff01;小伙伴们&#xff01;整个摄影后期行业都在用Portraiture&#xff0c;这是一个被奉为高级磨皮面板&#xff0c;修图神器、修图的的扩展面板&#xff01;Portraiture这款磨皮插件终于更新啦&#xff01;最近推出了Portraiture4.03版本,新版本光影处理更强大&…

《编程思维与实践》1066.最小不重复数

《编程思维与实践》1066.最小不重复数 题目 思路 一般在oj上循环 2 ⋅ 1 0 9 2\cdot 10^9 2⋅109次以上就会超时,所以由于这题的数据A可以很大,直接循环加一再判断会超时. 优化:首先可以明确要想使不重复数尽可能小,则高位数字应该尽可能小, 即先找到最靠前的两个重复数字,然后…

【Vector VN1630/40 I/O应用】-1-简易示波器

案例背景(共13页精简)&#xff1a;该篇博客将告诉您&#xff1a; Vector VN1630A&#xff0c;VN1640A&#xff0c;VH6501 I/O的使用&#xff1b;将Vector VN1630A/VN1640A CAN/LIN Interface的I/O接口充当一个简易的“示波器”使用&#xff1b;观察“CAN唤醒”工作的ECU控制器…

关于C语言的杂记4

文章目录 数据与程序结构C语言的编程机制#include <>和#include ""的区别形式参数和实际参数值传递地址传递 素数 文章内容摘自或加工于C技能树一些大佬的博文 数据与程序结构 阅读完C的编程机制和函数的声明和定义后的一些启发。——预处理 C语言的编程机制 …

dubbo技术

1、Dubbo的前世今生 2011年10月27日&#xff0c;阿里巴巴开源了自己的SOA服务化治理方案的核心框架Dubbo&#xff0c;服务治理和SOA的设计理念开始逐渐在国内软件行业中落地&#xff0c;并被广泛应用。 早期版本的dubbo遵循SOA的思想&#xff0c;是面向服务架构的重要组件。 …

1708_Simulink中取数组元素

全部学习汇总&#xff1a; GitHub - GreyZhang/g_matlab: MATLAB once used to be my daily tool. After many years when I go back and read my old learning notes I felt maybe I still need it in the future. So, start this repo to keep some of my old learning notes…

【多线程】线程安全问题原因与解决方案

目录 线程安全的概念 线程不安全示例 线程不安全的原因 多个线程修改了同一个变量 线程是抢占式执行的 原子性 内存可见性 有序性 线程不安全解决办法 synchronized 关键字-监视器锁monitor lock synchronized 的特性 互斥 刷新内存 可重入 synchronized 使用示例 Java 标…

【第三章:存储系统】

目录 知识框架No.0 引言No.1 存储器概述No.2 主存储器一、SRAM芯片和DRAM芯片二、只读存储器三、主存储器的基本组成1、基本的半导体元件和原理 知识框架 No.0 引言 这一章节主要是&#xff1a;这些二进制的数据在计算机内部如何存储 在学习这个章节之前&#xff0c;首先把下面…

Mybatis - 基础

文章目录 一、 Mybatis基本介绍二、 Mybatis 快速入门程序2.1 引入Mybatis依赖2.2 准备工作2.3 配置SQL信息2.3.1 IDEA连接数据库2.3.2 打开日志信息 2.4 JDBC 了解2.5 数据库连接池2.5.1 Druid数据库连接池 三、 Mybatis 基础3.1 环境准备3.1.1 数据库表3.1.2 实体类 3.2 基础…

EasyRecovery16中文最新版电脑数据恢复软件下载使用教程

EasyRecovery如果需要使用它来恢复数据&#xff0c;请注意&#xff0c;尤其是当需要恢复的数据文件非常重要时&#xff0c;建议使用软件EasyRecovery以保障数据安全。共有三个版本&#xff0c;分别是个人版、专业版、企业版&#xff0c;这三种都可以免费下载并使用&#xff0c;…

[MySQL]关于MySQL索引的一点点东西

最是人间留不住,朱颜辞镜花辞树. 目录 一.为什么需要索引 1.什么是索引 2.索引的创建原则 二.理解索引 1. MySQL与磁盘交互基本单位 2.索引的结构 a.关于hash索引,B树索引,B树索引的特点 B树 B树 HASH b.为什么使用InnoDB 存储引…

15-721 chapter 13 查询执行

优化的目标 CPU层面 cpu是多级流水线操作&#xff0c;所以我们的目标是让每个处理器的每个部分都处于busy。多条流水线&#xff0c;我们没有依赖的指令可以放到不同的流水线里面。但是流水线如果遇到branch判断错误的话&#xff0c;就要flush掉 可以用值传递来代替跳转 查询执…

Linux开发板安装Python环境

1. 环境介绍 硬件&#xff1a;STM32MP157&#xff0c;使用的是野火出的开发板。 软件&#xff1a;Debian ARM 架构制作的 Linux 发行版&#xff0c;版本信息如下&#xff1a; Linux发行版本&#xff1a;Debian GNU/Linux 10 内核版本&#xff1a;4.19.94 2. Python 简介…

基于常用设计模式的业务框架

前言 做开发也有好几年时间了&#xff0c;最近总结和梳理自己在工作中遇到的一些问题&#xff0c;工作中最容易写出BUG的需求就是改造需求了。一个成熟的业务系统是需要经过无数次迭代而成的&#xff0c;也意味着经过很多开发人员之手&#xff0c;最后到你这里&#xff0c;大部…

每日学术速递5.11

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Diffusion Explainer: Visual Explanation for Text-to-image Stable Diffusion 标题&#xff1a;扩散解释器&#xff1a;文本到图像稳定扩散的视觉解释 作者&#xff1a;Seongmin…

【数据结构】【算法】二叉树、二叉排序树、树的相关操作

树结构是以分支关系定义的一种层次结构&#xff0c;应用树结构组织起来的数据&#xff0c;逻辑上都具有明显的层次关系。 操作系统中的文件管理系统、网络系统中的域名管理、数据库系统中的索引管理等都使用了树结构来组织和管理数据。 树的基本概念 树Tree是由n个节点组成的有…

数据结构-查找-散列结构(散列表)

目录 *一、散列表 二、散列函数 *除留余数法 *直接定址法 数字分析法 平方取中法 三、冲突处理方法--开发定地法 *3.1线性探测法 *查找效率&#xff1a; *3.2平方探测法 3.3伪随机序列法 3.4再散列法 *一、散列表 又称哈希表&#xff0c;数据元素的关键字与其存储…

bgp团体属性配置案例一

RouterA的配置 sysname RouterA interface GigabitEthernet1/0/0 ip address 192.168.0.1 255.255.255.0 interface LoopBack0 ip address 1.1.1.1 255.255.255.255 bgp 10 router-id 1.1.1.1 //Router ID&#xff0c;建议配置为LoopBack0的IP地址 peer 192.168.0.2 as-number …

【Linux】信号的处理

信号篇终章 文章目录 前言一、信号的处理 1.可重入函数 2.volatile关键字 3.SIGCHLD信号总结 前言 在前两篇linux文章中我们详细的讲解了信号的产生和信号的保存&#xff0c;今天来到最后一个重点信号的处理&#xff0c;对于信号的处理我们会重新引入进程…

如何实现一个高效的H264信源编码器?了解核心算法和实现流程

H264 H264是一种常用的视频编码标准&#xff0c;它以网络传输和存储为设计目的&#xff0c;能够将视频信号进行高效压缩&#xff0c;并保持较高的视频质量。 H264视频编码标准采用的是基于帧的编码方式。每一帧视频都被分为不同的块&#xff0c;每个块中都包含了可压缩的信息…