【C++】STL之string的模拟实现

news2024/11/29 22:31:38

在本篇博客中,作者将会带领你模拟实现简单的STL中的string类的。至于string的常规使用,这里不做讲解。

string类的c++参考文档

string - C++ Reference (cplusplus.com)

 一.string的基本结构

string类的大致结构可以分为三个变量来表示,一个是字符指针,一个是size,还有一个是capacity。这三个变量分别表示:字符指针指向第一个字符串的第一个位置,size存放字符串的长度,capacity表示动态内存开辟空间的大小。如下图所示。

所以string类的成员变量可以像下面一样设计。 

namespace string_blog
{
	class string
	{
	public:

	private:
		char* _str;
		size_t size;
		size_t capacity;
	};
}

二.Member functions的实现

 Member functions可以分为三类:构造函数析构函数赋值=重载

1.构造函数 

构造函数又可以分为三种:无参构造函数有参构造函数拷贝构造 

无参构造函数和带参构造函数实现

首先说明,其实string类就是一个字符串数组,而在C语言中,字符串数组是用'\0'来结尾的,所以string类中的字符串也是用'\0'来结尾的。 

同时,由于C++支持缺省参数,所以无参构造函数和带参构造函数可以同时实现。

		//无参与带参构造函数
        //string s1;
		//string s2("hello world");
		string(const char* tmp = "")//缺省值给"",代表一个空字符串,而空字符串中只有一个\0
			:_str(new char[strlen(tmp)+1])//多开一个空间是为了存\0
			,_size(strlen(tmp))
			,_capacity(strlen(tmp)+1)
		{
			strcpy(_str, tmp);
		}

拷贝构造函数实现 

虽然我们不显式定义拷贝构造函数,编译器也会帮我们默认生成一个默认的拷贝构造函数但是这个拷贝构造函数是一个浅拷贝,浅拷贝在析构时会出现问题,原因可以看下面这篇博客,所以这里我们需要字节手动实现一个拷贝构造函数,来实现深拷贝。 

【C++】类与对象 (中篇)(6个默认成员函数)-CSDN博客

		//拷贝构造函数
		//string s2(s1);
		string(const string& tmp)
			:_str(new char[tmp.size()+1])//size函数是获取字符串长度,在后面实现
			,_size(tmp._size)
			,_capacity(tmp._capacity)
		{
			strcpy(_str, tmp._str);
		}

2.析构函数的实现 

		//析构函数
		~string()
		{
			delete[] _str;//释放_str指向的空间
			_str = nullptr;//把_str指针置成nullptr
			_size = _capacity = 0;
		}

3.赋值=重载

赋值=重载同样需要注意深浅拷贝问题。 

		//赋值=重载
		//string s1("hello world");
		//s1=s2;
		string& operator=(const string& tmp)
		{
			if (this != &tmp)//防止自己给自己赋值
			{
				delete[] _str;//先释放原来的字符串
				char* ptr = new char[tmp.size() + 1];//开一块新空间给_str
				strcpy(ptr, tmp._str);//将tmp的内容拷贝给ptr
				_str = ptr;
				_size = tmp._size;
				_capacity = tmp._capacity;
			}
			return *this;
		}

三.Iterators的实现

 迭代器可以分为四种:正向的,反向的,const的,非const的,在这里先不讨论反向迭代器

对于string类来说,迭代器的底层其实就是一个char类型的指针,所以string的迭代器可以这样实现。 

		//对char* 重命名为iterator迭代器
		typedef char* iterator;
		typedef const char* const_iterator;

		//begin迭代器
		iterator begin()
		{
			return _str;//返回第一个字符的指针
		}

		//end迭代器
		iterator end()
		{
			return _str + size();//返回\0位置的指针
		}

		//const begin迭代器
		const_iterator begin() const
		{
			return _str;
		}

		//const end迭代器
		const_iterator end() const
		{
			return _str + size();
		}

四.capacity的实现

 capacity有以上这么多个函数,我们都来把常用的实现一下。

1.size、length和capacity的函数实现

其实size和length本质上是同一个功能,都是获取字符串的长度,只不过函数名不同而已,所以我们在这里只实现size。同时,capacity是去获取字符串的容量

		//返回string的字符个数
		size_t size() const
		{
			return _size;
		}

		//返回string的容量
		size_t capacity() const
		{
			return _capacity;
		}

2. resize和reserve的实现

resize是将字符串大小调整到n个字符的长度,        reserve是调整string的容量大小

reserve的实现

在这里,我们先实现reserve函数,这有助于我们在实现resize时,去复用reserve。 

		//调整string的容量
		void reserve(const size_t n = 0)
		{
			if (n > size())//如果要调整到的容量大于原来的容量,才进行调整
			{
				char* tmp = new char[n + 1];//预留一个位置存放\0
				strcpy(tmp, _str);//将原来的字符串拷贝到新空间中
				delete[] _str;//释放原来的空间
				_str = tmp;//把新空间_str
				_capacity = n;
			}
		}

resize的实现

 resize的实现要分下面这三种情况

		//调整string的size
		void resize(const size_t n, const char& ch = '\0')
		{
			if (n > _size)
			{
				if (n > _capacity)//如果size大于容量,则需要扩容
				{
					reserve(n);//扩容到n个容量
				}

				size_t left = _size;
				size_t right = n;
				while (left < right)//将ch字符内容赋值到没有内容的位置中
				{
					_str[left++] = ch;
				}
			}
			//将最后一个位置赋值\0
			_str[n] = '\0';
		}

3.clear和empty的实现

clear清空string中所有的内容,empty判断string是否为空。 

		//clear实现
		void clear()
		{
			_str[0] = '\0';//将第一个字符的位置给\0
			_size = 0;//并将长度给0
		}

		//empty实现
		bool empty() const 
		{
			if (size() == 0)
				return true;
			else
				return false;
		}

五. Element access的实现

 operator[]与at的作用时一样的,都是获取第n个位置字符的引用,所有这里只实现operator[]

		//重载[]
		char& operator[](const size_t pos)
		{
			assert(pos < size());//如果要返回的位置不存在,则断言
			return *(_str + pos);
		}

		//重载const[]
		const char& operator[](const size_t pos) const
		{
			assert(pos < size());//如果要返回的位置不存在,则断言
			return *(_str + pos);
		}

注意,重载[]有两个,一个是非const的,可读可写,一个是const的,只能读。 

 六.Modifiers的实现

 在实现这些函数时,我们可以优先实现insert和erase,这样在后面尾插尾删时可以复用insert和erase。

1.insert和erase的实现 

		//在pos位置插入一个字符串
		void insert(const size_t pos, const char* s)
		{
			assert(pos <= size());//断言 插入的位置要<=size

			if ((size() + strlen(s)) > capacity())//判断追加字符串是否需要扩容
			{
				reserve(size() + strlen(s));
			}

			//将pos位置后的字符串后挪
			memmove(_str + pos + strlen(s), _str + pos, end() - (_str + pos));
			//将s字符串插入到pos的位置
			memmove(_str + pos, s, strlen(s));
			//将最后一个位置赋值\0
			*(_str + capacity()) = '\0';
		}

		//在pos位置插入n个字符
		void insert(const size_t pos, size_t n, const char& ch)
		{
			assert(pos <= size());
			if ((size() + n) > capacity())
			{
				reserve(size() + n);
			}
			memmove(_str + pos + n, _str + pos, end() - (_str + pos));
			memset(_str + pos, ch, n);
			*(_str + capacity()) = '\0';
		}

		//在pos位置删除n个字符
		void erase(const size_t pos, size_t len)
		{
			assert(pos < size());
			
			if (len >= (end() - (_str + pos)))//如果要删除的长度长于pos后面的字符串长度,则直接删到尾
			{
				*(_str + pos) = '\0';
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
			}
		}

2.push_back、pop_back和append的实现

这几个函数的实现,可以直接复用insert和erase。 

		//尾插一个字符
		void push_back(const char& ch)
		{
			insert(size(), 1, ch);
		}

		//尾删一个字符
		void pop_back()
		{
			assert(size());
			erase(size() - 1, 1);
		}

		//尾插一个字符串
		void append(const char* s)
		{
			insert(size(), s);
		}

3.operator+=的实现

operator+=其实就是尾插的意思,这里我直接复用push_back和append就行。 

		//重载operator+=
		string& operator+=(const char* s)
		{
			append(s);
			return *this;
		}

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

 七.String operations的实现

在这里我们只挑最常用的几个来实现。 

1.c_str的实现

c_str是返回一个C语言形式的字符串,即一个char类型的指针。 

		//转换为C形字符串
		char* c_str() const
		{
			return _str;
		}

2.find的实现

find函数是一个字符串查找函数,在模拟实现里面,我们可以调用C语言的strstr来实现。 

		//字符串查找    从第pos个位置开始查找
		int find(const char* s, const size_t pos =0) const
		{
			char* tmp = strstr(_str + pos, s);
			if (tmp == nullptr)
			{
				return -1;
			}
			else
			{
				return tmp - _str;
			}
		}

八.Non-member function overloads的实现

 最后是一些字符串比较和重载输入输出的实现。

1.各种比较的实现 

		bool operator==(const char* s) const 
		{
			int ret = strcmp(_str, s);
			if (ret == 0)
				return true;
			else
				return false;
		}

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

		bool operator>(const char* s) const
		{
			int ret = strcmp(_str, s);
			if (ret > 0)
				return true;
			else
				return false;
		}

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

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

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

2.重载输入和输出

	//重载cout<<
	ostream& operator<<(ostream& out, string& s)
	{
		out << s.c_str() << endl;
		return out;
	}

	//重载cin>>
	istream& operator>>(istream& in, string& s)
	{
		//输入之前,需要清空字符串
		s.clear();
		while (1)//进去循环,一个一个字符的插入
		{
			char ch;
			//cin >> ch;
			ch = in.get();//读取一个字符
			if (ch == ' ' || ch == '\n')//判断这个字符是否结束
			{
				break;
			}
			else
			{
				s += ch;
			}
		}
		s += '\0';
		return in;
	}

3.getline的实现

 getline也是实现字符串的输入,但是getline和operator>>的区别是,getline可以输入空格

operator>>不能输入空格。

		void getline()
		{
			//输入之前,需要清空字符串
			clear();
			while (1)
			{
				char ch;
				//cin >> ch;
				ch = cin.get();
				if (ch == '\n')
				{
					break;
				}
				else
				{
					(*this) += ch;
				}
			}
			(*this) += '\0';
		}

写到这里,我们模拟实现的string类基本上就已经完成了,当然,STL库中的string类不止这么点接口,但是有很多是很相似的,有兴趣的朋友可以继续完善。 

九.所有源代码

string.h文件

namespace string_blog
{
	class string
	{
	public:
		//对char* 重命名为iterator迭代器
		typedef char* iterator;
		typedef const char* const_iterator;
	public:
		//无参与带参构造函数
		//string s1;
		//string s2("hello world");
		string(const char* tmp = "")//缺省值给"",代表一个空字符串,而空字符串中只有一个\0
			:_str(new char[strlen(tmp)+1])//多开一个空间是为了存\0
			,_size(strlen(tmp))
			,_capacity(strlen(tmp))
		{
			strcpy(_str, tmp);
		}

		//拷贝构造函数
		//string s2(s1);
		string(const string& tmp)
			:_str(new char[tmp.size()+1])//size()函数在后面实现
			,_size(tmp._size)
			,_capacity(tmp._capacity)
		{
			strcpy(_str, tmp._str);
		}

		//析构函数
		~string()
		{
			delete[] _str;//释放_str指向的空间
			_str = nullptr;//把_str指针置成nullptr
			_size = _capacity = 0;
		}

		//赋值=重载
		//string s1("hello world");
		//s1=s2;
		string& operator=(const string& tmp)
		{
			if (this != &tmp)//防止自己给自己赋值
			{
				delete[] _str;//先释放原来的字符串
				char* ptr = new char[tmp.size() + 1];//开一块新空间给_str
				strcpy(ptr, tmp._str);//将tmp的内容拷贝给ptr
				_str = ptr;
				_size = tmp._size;
				_capacity = tmp._capacity;
			}
			return *this;
		}

		//begin迭代器
		iterator begin()
		{
			return _str;//返回第一个字符的指针
		}

		//end迭代器
		iterator end()
		{
			return _str + size();//返回\0位置的指针
		}

		//const begin迭代器
		const_iterator begin() const
		{
			return _str;
		}

		//const end迭代器
		const_iterator end() const
		{
			return _str + size();
		}

		//返回string的字符个数
		size_t size() const
		{
			return _size;
		}

		//返回string的容量
		size_t capacity() const
		{
			return _capacity;
		}

		//调整string的容量
		void reserve(const size_t n = 0)
		{
			if (n > size())//如果要调整到的容量大于原来的容量,才进行调整
			{
				char* tmp = new char[n + 1];//预留一个位置存放\0
				strcpy(tmp, _str);//将原来的字符串拷贝到新空间中
				delete[] _str;//释放原来的空间
				_str = tmp;//把新空间_str
				_capacity = n;
			}
		}

		//调整string的size
		void resize(const size_t n, const char& ch = '\0')
		{
			if (n > _size)
			{
				if (n > _capacity)//如果size大于容量,则需要扩容
				{
					reserve(n);//扩容到n个容量
				}

				size_t left = _size;
				size_t right = n;
				while (left < right)//将ch字符内容赋值到没有内容的位置中
				{
					_str[left++] = ch;
				}
			}
			//将最后一个位置赋值\0
			*_str = '\0';
		}

		//clear实现
		void clear()
		{
			_str[0] = '\0';//将第一个字符的位置给\0
			_size = 0;//并将长度给0
		}

		//empty实现
		bool empty() const 
		{
			if (size() == 0)
				return true;
			else
				return false;
		}

		//重载[]
		char& operator[](const size_t pos)
		{
			assert(pos < size());//如果要返回的位置不存在,则断言
			return *(_str + pos);
		}

		//重载const[]
		const char& operator[](const size_t pos) const
		{
			assert(pos < size());//如果要返回的位置不存在,则断言
			return *(_str + pos);
		}

		//尾插一个字符
		void push_back(const char& ch)
		{
			insert(size(), 1, ch);
		}

		//尾删一个字符
		void pop_back()
		{
			assert(size());
			erase(size() - 1, 1);
		}

		//尾插一个字符串
		void append(const char* s)
		{
			insert(size(), s);
		}

		//重载operator+=
		string& operator+=(const char* s)
		{
			append(s);
			return *this;
		}

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

		//在pos位置插入一个字符串
		void insert(const size_t pos, const char* s)
		{
			assert(pos <= size());//断言 插入的位置要<=size

			if ((size() + strlen(s)) > capacity())//判断追加字符串是否需要扩容
			{
				reserve(size() + strlen(s));
			}

			//将pos位置后的字符串后挪
			memmove(_str + pos + strlen(s), _str + pos, end() - (_str + pos));
			//将s字符串插入到pos的位置
			memmove(_str + pos, s, strlen(s));
			//将最后一个位置赋值\0
			*(_str + capacity()) = '\0';
			_size = size() + strlen(s);//调整新_size值
		}

		//在pos位置插入n个字符
		void insert(const size_t pos, size_t n, const char& ch)
		{
			assert(pos <= size());
			if ((size() + n) > capacity())
			{
				reserve(size() + n);
			}
			memmove(_str + pos + n, _str + pos, end() - (_str + pos));
			memset(_str + pos, ch, n);
			*(_str + capacity()) = '\0';
			_size = _size + n;
		}

		//在pos位置删除n个字符
		void erase(const size_t pos, size_t len)
		{
			assert(pos < size());
			
			if (len >= (end() - (_str + pos)))//如果要删除的长度长于pos后面的字符串长度,则直接删到尾
			{
				*(_str + pos) = '\0';
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
			}

			_size = _size - len;
		}

		//字符串查找    从第pos个位置开始查找
		int find(const char* s, const size_t pos =0) const
		{
			char* tmp = strstr(_str + pos, s);
			if (tmp == nullptr)
			{
				return -1;
			}
			else
			{
				return tmp - _str;
			}
		}

		//转换为C形字符串
		char* c_str() const
		{
			return _str;
		}

		bool operator==(const char* s) const 
		{
			int ret = strcmp(_str, s);
			if (ret == 0)
				return true;
			else
				return false;
		}

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

		bool operator>(const char* s) const
		{
			int ret = strcmp(_str, s);
			if (ret > 0)
				return true;
			else
				return false;
		}

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

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

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

		void getline()
		{
			//输入之前,需要清空字符串
			clear();
			while (1)
			{
				char ch;
				//cin >> ch;
				ch = cin.get();
				if (ch == '\n')
				{
					break;
				}
				else
				{
					(*this) += ch;
				}
			}
			(*this) += '\0';
		}

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

	//重载cout<<
	ostream& operator<<(ostream& out, string& s)
	{
		out << s.c_str() << endl;
		return out;
	}

	//重载cin>>
	istream& operator>>(istream& in, string& s)
	{
		//输入之前,需要清空字符串
		s.clear();
		while (1)
		{
			char ch;
			//cin >> ch;
			ch = in.get();
			if (ch == ' ' || ch == '\n')
			{
				break;
			}
			else
			{
				s += ch;
			}
		}
		s += '\0';
		return in;
	}

	//测试构造函数 和 赋值=重载
	void Test1()
	{
		string s1;
		string s2("hello world");
		string s3(s2);
		s1 = s3;

		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
		cout << s3.c_str() << endl;
	}

	//测试迭代器
	void Test2()
	{
		string s1("hello world");
		string::iterator it = s1.begin();
		while (it != s1.end())
		{
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}

	//测试
	void Test3()
	{
		string s1("hello world");
		s1.clear();
		s1.resize(10, 'b');
		cout << s1.c_str() << endl;
		cout << s1.empty() << endl;
		s1.clear();
		cout << s1.c_str() << endl;
		cout << s1.empty() << endl;
	}
    
    //测试operator[]
	void Test4()
	{
		string s1("hello world");
		for (int i = 0; i < s1.size(); i++)
		{
			s1[i] -= 1;
		}
		cout << s1.c_str() << endl;
	}

	//测试insert
	void Test5()
	{
		string s1("hello world");
		s1.getline();
		cout << s1 << endl;
	}
}

string.c文件

#include"string.h"
using namespace string_blog;

int main()
{
	string_blog::Test1();
	string_blog::Test2();
	string_blog::Test3();
	string_blog::Test4();
	string_blog::Test5();

	return 0;
}

十.拷贝构造和赋值=重载的现代写法

在做完上面的工作后,string类的模拟实现也基本完成了,而现在在补充一下拷贝构造和赋值=重载的现代写法

什么是现代写法?

在上面的写法中,是比较常规的写法,但是,在现在,我们可以这样写。 

1.拷贝构造现代写法

		void Swap(string& s)//交换函数,交换两个string类对象
		{
			swap(_str, s._str);
			swap(_size, s._size);
			swap(_capacity, s._capacity);
		}

		//拷贝构造现代写法
		string(const string& s)
			:_str(nullptr)
		{
			string tmp(s._str);//用s的字符串去构造tmp
			Swap(tmp);//用tmp和this进行交换
		}

2.赋值=重载的现代写法

		//赋值=重载的现代写法
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				string tmp(s);//用s去构造一个临时对象
				Swap(tmp);//交换两个string对象
			}
			return *this;
		}

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

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

相关文章

UE5 HLSL 详细学习笔记

这里的POSITION是变量Position的语义&#xff0c;告诉寄存器&#xff0c;此变量的保存位置&#xff0c;通常语义用于着色器的输入和输出&#xff0c;以冒号“&#xff1a;”的方式进一步说明此变量&#xff0c;COLOR也类似 还有什么语义呢&#xff1f; HLSL核心函数&#xff1a…

js如何将对象转换成数组

方法一&#xff1a;Object.values(obj) 示例&#xff1a; var obj { name: 小明, age: 22 }; console.log( Object.values(obj) ); // [小明, 22] 方法二&#xff1a;for-in循环 示例&#xff1a; var obj { name: 小明, age: 22 }; var arr []; let objName []; for(l…

Spingbot项目配置mySQL或postgresSQL详解

1&#xff1a;postgresql库: yml文件 探索PostgreSQL&#xff1a;从基础到实践&#xff08;简单实例&#xff09; # PageHelper分页插件 pagehelper:helperDialect: postgresqlreasonable: truesupportMethodsArguments: trueparams: countcountSql# 数据源配置 spring:datas…

ArcGIS无法链接在线地图或错误: 代理服务器从远程服务器收到了错误地址(验证服务器是否正在运行)。

这几天我们分享了&#xff01; 谷歌卫星影像图归来&#xff01;ArcGIS直连&#xff01;快来获取_谷歌影像lyr-CSDN博客文章浏览阅读666次&#xff0c;点赞11次&#xff0c;收藏9次。大概。_谷歌影像lyrhttps://blog.csdn.net/kinghxj/article/details/137521877一套图源搞定&a…

LPA算法简介

1. 背景 标签传播算法(Label Propagation Algorithm)是一种基于图的半监督学习方法&#xff0c;其基本思路是用已标记节点的标签信息去预测未标记节点的标签信息。 2. 算法流程 1. 为每个节点随机的指定一个自己特有的标签&#xff1b; 2. 逐轮刷新所有节点的标签&#xff0…

音乐小程序|基于微信开发音乐小程序的系统设计与实现(源码+数据库+文档)

音乐小程序目录 基于微信开发音乐小程序的系统 一、前言 二、系统设计 三、系统功能设计 小程序端&#xff1a; 后台 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师…

开箱即用之 windows装的MongoDB 服务无法开机自启

先把mongo服务手动停止 以管理员的身份打开黑窗口&#xff0c;并移除系统服务 进入mongod.exe文件所在目录 mongod.exe --remove --serviceName "MongoDB" 在你mongo的data目录下&#xff0c;移除两个文件 mongod.lock strage.bson 重新将服务加入开机自启的系…

测试使用Python GDAL 下载Mapbox瓦片地图及拼接

测试使用 Python GDAL 下载 Mapbox 瓦片地图及拼接 本教程将展示如何以编程方式从网络地图&#xff08;通常称为瓦片地图&#xff09;瓦片服务器下载地图图像&#xff0c;对其进行地理参考&#xff08;设置坐标系&#xff09;并将其保存为GeoTIFF。 Code import lib #!/usr…

2011-2022年上市公司新质生产力测算数据(含原始数据+计算代码+计算结果)

2011-2022年上市公司新质生产力测算数据&#xff08;含原始数据计算代码计算结果&#xff09; 1、时间&#xff1a;2011-2022年 2、来源&#xff1a;原始数据整理自csmar、wind 3、指标&#xff1a;证券代码、证券简称、统计截止日期、报表类型、营业收入、研发费用、资产减…

一起Talk Android吧(第五百五十七回:如何获取文件读写权限)

文章目录 1. 概念介绍2. 使用方法3. 示例代码4. 内容总结各位看官们大家好,上一回中分享了一个Retrofit使用错误的案例,本章回中将介绍 如何获取文件读写权限。闲话休提,言归正转,让我们一起Talk Android吧! 1. 概念介绍 我们在本章回中说的文本读写权限是指读写手机中的…

说说你对树的理解?相关的操作有哪些?

一、是什么 在计算机领域&#xff0c;树形数据结构是一类重要的非线性数据结构&#xff0c;可以表示数据之间一对多的关系。以树与二叉树最为常用&#xff0c;直观看来&#xff0c;树是以分支关系定义的层次结构 二叉树满足以下两个条件&#xff1a; 本身是有序树树中包含的…

在Linux系统中设定延迟任务

一、在系统中设定延迟任务要求如下&#xff1a; 要求&#xff1a; 在系统中建立easylee用户&#xff0c;设定其密码为easylee 延迟任务由root用户建立 要求在5小时后备份系统中的用户信息文件到/backup中 确保延迟任务是使用非交互模式建立 确保系统中只有root用户和easylee用户…

OpenAI Token计算方式

如果用 ChatGPT API 去做问答的话是需要付费的&#xff0c;OpenAI 的收费方式是通过 token 数量进行收费&#xff0c;API 价格根据不同模型有所不同&#xff0c;可以看到 GPT4 最贵&#xff0c;GPT3.5 最便宜。这让我想起以前用 Aliyun 中台&#xff0c;每个 SQL 都有个运行价格…

部署Zabbix5.0

一.部署zabbix客户端 端口号10050 zabbix 5.0 版本采用 golang 语言开发的新版本客户端 agent2 。 zabbix 服务端 zabbix_server 默认使用 10051 端口&#xff0c;客户端 zabbix_agent2 默认使用 10050 端口。 1.1.关闭防火墙和selinux安全模块 systemctl disable --now fir…

数据库工具解析之 OceanBase 数据库导出工具

背景 大多数的数据库都配备了自己研发的导入导出工具&#xff0c;对于不同的使用者来说&#xff0c;这些工具能够发挥不一样的作用。例如&#xff1a;DBA可以使用导数工具进行逻辑备份恢复&#xff0c;开发者可以使用导数工具完成系统间的数据交换。这篇文章主要是为OceanBase…

Linux——操作系统与进程基本概念

Linux——操作系统与进程基本概念 文章目录 Linux——操作系统与进程基本概念一、冯诺依曼体系结构二、操作系统2.1 OS层次图2.2 操作系统的作用2.3 管理的理解 三、进程3.1 进程的概念3.2 描述进程—PCB3.3 PCB的内容3.3.1 查看进程3.3.2 标识符3.3.3 状态3.3.4 程序计数器3.3…

react中关于类式组件和函数组件对props、state、ref的使用

文章中有很多蓝色字体为扩展链接&#xff0c;可以补充查看。 常用命令使用规则 组件编写方式: 1.函数式 function MyButton() { //直接return 标签体return (<>……</>); }2.类 class MyButton extends React.Component { //在render方法中&#xff0c;return…

matlab关于COE文件之读取操作

平台&#xff1a;matlab2021b 场景&#xff1a;在使用fir滤波器后&#xff0c;我们使用matlab生成coe文件后。在xilinx新建IP的后&#xff0c;数据流经过FIR的IP核后数据位宽变宽。这时候我们需要对数据进行截位。这时候需要读取coe文件求和后&#xff0c;计算我们需要截位的位…

Postman之版本信息查看

Postman之版本信息查看 一、为何需要查看版本信息&#xff1f;二、查看Postman的版本信息的步骤 一、为何需要查看版本信息&#xff1f; 不同的版本之间可能存在功能和界面的差异。 二、查看Postman的版本信息的步骤 1、打开 Postman 2、打开设置项 点击页面右上角的 “Set…

数据加密、文档加密为什么都选择安企神软件

数据加密、文档加密为什么都选择安企神软件 免费试用安企神 在数据加密和文件加密领域&#xff0c;有众多优秀的软件&#xff0c;他们功能各异、价格不同、效果也大相径庭&#xff0c;经过对比使用、用户口碑和技术网站评判&#xff0c;安企神在各方面都稳坐第一把交易。其原…