C++:String的模拟实现

      

     模拟实现的节奏比较快,大家可以先去看看博主的关于string的使用,然后再来看这里的模拟实现过程

C++:String类的使用-CSDN博客

      String模拟实现大致框架迭代器以及迭代器的获取(public定义,要有可读可写的也要有可读不可写的)/成员变量(private定义)  并且为了不和库的string冲突,我们需要自己搞一个命名空间

namespace cyx
{
	class string
	{
	public:
		//迭代器的实现(可读可写)
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		//迭代器的实现(可读不可写)
		typedef const char* const_iterator;
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
		static const size_t npos;
		//static const size_t npos=-1  vs下int类型支持给static和const同时修饰的变量用缺省值
	};
	 const size_t string::npos = -1;//静态成员遍历类外初始化
}

      nops是一个静态变量,要类内定义类外初始化,由于nops是size_t类型,赋值-1会被强转成最大的无符号整数

一、构造+析构+赋值重载(Member functions)

1.1 全缺省构造函数

//构造函数
string(const char* str = "")
	:_size(strlen(str))
	{
		_capacity = _size == 0 ? 3 : _size;
		_str = new char[_capacity + 1];//   多开一块\0的空间
		strcpy(_str, str);
	}

1、 “ ”空字符串其实里面默认就有\0,所以缺省值直接给空字符串就行

2、_capacity 一定要给初始空间,不然后面如果涉及到2倍扩容,为0的话就扩不了了

3、要多开一块空间,连这个\0 

4、可以复用strcpy函数

1.2 拷贝构造函数的传统写法

     传统的思路就是拷贝,也就是我们先根据被拷贝的对象的_capacity开空间,然后再进行拷贝

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

1.3 拷贝构造函数的现代写法和swap函数

       现代的思路就是,尝试去复用,比如说我们可不可以直接去利用前面的构造函数去构造一个新对象,然后再窃取新对象的成果(利用swap)

//交换字符串
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)
		{
			string temp(s._str);
			swap(temp);
		}

 传统写法和现代写法参数一样,不能重载,只能保留一个 

1. 4 迭代器区间构造

		//迭代器区间构造
		template <class InputIterator>
		string(InputIterator first, InputIterator last)
			:_str(new char[last-first+1])
			,_size(0)
			,_capacity(last-first)
		{
			while (first != last)
			{
				push_back (*first);
				++first;
			}
		}

       这里定义的模版InputIterator的意思其实是这边我们可以传不同类型对象的迭代器,我们并不知道这个迭代器里面有多少元素,所以得用指针-指针,即last-first来确定我们的容量,然后再开空间,一个个进行尾插。

1.5 赋值重载的传统写法

       传统的思路就是,先开一块新空间拷贝旧数据,然后再释放掉原空间,这里尽量是先开空间再释放,避免我们开空间失败导致原始数据的丢失。   

		//赋值重载(传统写法)
		string& operator=(const string& s)
		{
			if (this != &s)//避免自赋值
			{
				//先开新空间再毁旧空间,避免新空间开失败导致数据丢失
				char* temp = new char[s._capacity + 1];
				strcpy(temp, s._str);
				delete[]_str;
				_str = temp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}

注意:要注意自赋值情况!!否则刚拷贝完自己就被释放了

1.6 赋值重载的现代写法

      现代的思路就是,既然被赋值这个空间不想要,那就和形参直接交换吧!!但是要注意的是,这里就不能像传统的一样用const引用了,否则不想要的空间就给到我们的赋值对象了,这边就得用传值传参,这样被交换的就只是一个临时拷贝,不想要的空间随着栈帧的结束被销毁。

//赋值重载(现代写法)
string& operator=(string s)//必须用值传递,否则会导致原数据的丢失
{
	swap(s);
	return *this;
}

  传统写法和现代写法参数不一样,一个是const引用,一个传值传参,所以可以同时存在。

1.7 析构函数

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

 二、容量相关的接口(Capacity)

2.1 size、capacity

	//获取当前size
	size_t size() const
	{
		return _size;
	}
	//获取当前capacity
	size_t capacity() const
	{
		return _capacity;
	}

2.2 reserve

如果n比_capacity大,思路就是先开新空间进行拷贝,然后再释放旧空间

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

2.3 resize

如果n比_size大,我们根据n去扩容,然后因为string的底层是字符数组,所以memset就很适合,他就是可以去一个字节一个字节设置成我们想要的。缺省值给‘\0’

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

2.4 clear和empty

//清理字符串
void clear()
{
	_str[0] = '\0';
	_size = 0;
}
//判断字符串是否为空
bool empty()
{
	return _capacity == 0;
}

2.5 shrink_to_fit(用得少)

缩容到size位置,平时用的很少,我们要尽量减少扩容,思路也是一样的,开辟新空间去拷贝,再释放旧空间

	void shrink_to_fit()
	{
		char* temp = new char[_size + 1];
		strcpy(temp, _str);
		delete[] _str;
		_str = temp;
		_capacity = _size;
	}

三、[ ]和比较运算符重载

//[]重载(可读可写)
char& operator[](size_t pos)
{
	assert(pos < _size);//确保地址有效
	return _str[pos];
}
//[]重载(可读不可写)
const char& operator[](size_t pos) const
{
	assert(pos < _size);//确保地址有效
	return _str[pos];
}
//比较类型重载
//>
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);
}

有了[ ]、迭代器,我们可以展示3种遍历方法:下标访问、迭代器区间访问、范围for访问

	void Print(const string& s)
	{
	  //下标遍历
		for (size_t i = 0; i < s.size(); ++i)
			cout << s[i] << " ";
		cout << endl;
	  //迭代器遍历访问
		string::const_iterator it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	  //范围for
		for (auto &e : s)
			cout << e << " ";
		cout << endl;
	}

四、增删接口(Modifiers)

       字符串的增删接口一般要设置两个版本,一个是操作字符,一个是操作字符串,我们先把最难的insert和erase搞了,其他的就可以复用了

4.1 insert

	//指定位置插入一个字符
	string& insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		//判断是否需要扩容
		if (_size + 1 > _capacity)
			reserve(2 * _capacity);
		//pos后的数据要往后挪,所以要从后往前移
		size_t end = _size + 1;
		while (end > pos)
		{
			_str[end] = _str[end - 1];
			--end;
		}
		_str[pos] = ch;
		++_size;
		return *this;
	}
	//指定位置插入一个字符串
	string& insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		//判断是否需要扩容
		size_t len = strlen(str);
		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, len);
		_size += len;
		return *this;
	}

4.2 erase

//删除指定位置之后的字符
string& erase(const size_t pos, size_t len = npos)
{
	assert(pos <= _size);
	//有两种情况,一种是全删完,一种是删中间的一部分
	//全删完
	if (len == npos || pos + len > _size)//len == npos必须写,因为nops是无符号最大值,+的话会溢出
	{
		_str[pos] = '\0';
		_size = pos;
	}
	//删一部分
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
	return *this;
}

 len == npos这个判断条件必须写,因为nops已经是无符号最大值了,再+会溢出

4.3 push_back

	//尾插一个字符
	void push_back(char ch)
	{
		insert(_size, ch);
	}

4.4 append

//尾插一个字符串
string& append(const char* str)
{
	return insert(_size, str);
}

4.5 +=重载(用的多)

	//+=重载 字符
	string& operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	//+=重载 字符串
	string& operator+=(const char* str)
	{
		append(str);
		return *this;
	}

五、字符串操作(String operations

5.1 获取c类型字符串

//获取c类型字符串
const char* c_str() const
{
	return _str;
}

5.2 寻找指定字符串并返回下标

	//寻找指定字符串并返回下标
	size_t find(const char* str, size_t pos = 0)const
	{
		assert(pos < _size);
		char* p = strstr(_str + pos, str);
		if (p == nullptr)
			return npos;
		return p - _str;
	}

六、重载流插入和流提取 

我们不能写在类内,否则会*this会占用第一个操作数,不符合我们的使用习惯。

6.1 流提取

//重载<<
std::ostream& operator<< (std::ostream& out, const string& s)
{
	//可以用范围for,也可以用迭代器   范围for是用宏写的,本质上也是迭代器!
	for (auto ch : s)
		out << ch;
	return out;
}

6.2 流插入

首先我们要知道两点,1.>>只会读取到空格或者换行结束 2.读取前会清理掉原空间的数据

	//重载>>
	std::istream& operator>> (std::istream& in, string& s)
	{
		//读取前要先清理掉原来存在的字符
		s.clear();
		//用get获取字符
		char ch = in.get();
		//先用一个数组存起来,再一起加
		char buff[128];
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			//原始方法,一个字符一个字符加太麻烦,先用一个数组存起来,再一起加
			//s += ch;
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				s += buff;
				i = 0;//重置i
			}
			ch = in.get();
		}
		//循环结束后可能还要一些字母没有存进去
		if (i != 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}

我们用一个buff数组来暂时存储需要插入的字符,等存完了再+=,这样可以提高效率,以空间换时间 

七、string模拟实现全部代码

namespace cyx
{
	using std::endl;
	using std::cout;
	class string
	{
	public:
		//迭代器的实现(可读可写)
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		//迭代器的实现(可读不可写)
		typedef const char* const_iterator;
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}
		//交换字符串
		void swap(string& s)
		{
			std::swap(_str, s._str);//浅拷贝,没有开空间,只是改变指针指向
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		//构造函数
		string(const char* str = "")
			:_size(strlen(str))
			{
				_capacity = _size == 0 ? 3 : _size;
				_str = new char[_capacity + 1];//   多开一块\0的空间
				strcpy(_str, str);
			}
		//拷贝构造函数(传统写法)
		/*string(const string& s)
			:_size(s._size)
			, _capacity(s._capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}*/
        //拷贝函数的现代写法
		string(const string& s)
			:_str(nullptr)
		{
			string temp(s._str);
			swap(temp);
		}
		//迭代器区间构造
		template <class InputIterator>
		string(InputIterator first, InputIterator last)
			:_str(new char[last-first+1])
			,_size(0)
			,_capacity(last-first)
		{
			while (first != last)
			{
				push_back (*first);
				++first;
			}
		}
		//赋值重载(传统写法)
		string& operator=(const string& s)
		{
			if (this != &s)//避免自赋值
			{
				//先开新空间再毁旧空间,避免新空间开失败导致数据丢失
				char* temp = new char[s._capacity + 1];
				strcpy(temp, s._str);
				delete[]_str;
				_str = temp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}
		//赋值重载(现代写法)
		string& operator=(string s)//必须用值传递,否则会导致原数据的丢失
		{
			swap(s);
			return *this;
		}
		//析构函数
		~string()
		{
			delete[]_str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		//获取c类型字符串
		const char* c_str() const
		{
			return _str;
		}
		//获取当前size
		size_t size() const
		{
			return _size;
		}
		//获取当前capacity
		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];
		}
		//比较类型重载
		//>
		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
		void resize(size_t n, char ch = '\0')
		{
			if (n > _size)
			{
				reserve(n);
				memset(_str + _size, ch, n - _size);
			}
			_size = n;
			_str[_size] = '\0';
		}
		//改变capacity
		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 ch)
		{
			insert(_size, ch);
		}
		//尾插一个字符串
		string& append(const char* str)
	    {
			return insert(_size, str);
		}
		//+=重载 字符
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		//+=重载 字符串
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
		//指定位置插入一个字符
		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			//判断是否需要扩容
			if (_size + 1 > _capacity)
				reserve(2 * _capacity);
			//pos后的数据要往后挪,所以要从后往前移
			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}
			_str[pos] = ch;
			++_size;
			return *this;
		}
		//指定位置插入一个字符串
		string& insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			//判断是否需要扩容
			size_t len = strlen(str);
			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, len);
			_size += len;
			return *this;
		}
		//删除指定位置之后的字符
		string& erase(const size_t pos, size_t len = npos)
		{
			assert(pos <= _size);
			//有两种情况,一种是全删完,一种是删中间的一部分
			//全删完
			if (len == npos || pos + len > _size)//len == npos必须写,因为nops是无符号最大值,+的话会溢出
			{
				_str[pos] = '\0';
				_size = pos;
			}
			//删一部分
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}
		//寻找指定字符并返回下标
		size_t find(char ch, size_t pos = 0)const
		{
			assert(pos < _size);
			for (size_t i = pos; i < _size; ++i)
				if (_str[i] == ch)
					return i;
			return npos;
		}
		//寻找指定字符串并返回下标
		size_t find(const char* str, size_t pos = 0)const
		{
			assert(pos < _size);
			char* p = strstr(_str + pos, str);
			if (p == nullptr)
				return npos;
			return p - _str;
		}
		//清理字符串
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		//缩容到他的size
		void shrink_to_fit()
		{
			char* temp = new char[_size + 1];
			strcpy(temp, _str);
			delete[] _str;
			_str = temp;
			_capacity = _size;
		}
		//判断字符串是否为空
		bool empty()
		{
			return _capacity == 0;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
		static const size_t npos;
		//static const size_t npos=-1       vs下int类型支持给static和const同时修饰的变量用缺省值
	};
	 const size_t string::npos = -1;//静态成员遍历类外初始化
	//重载<<
	std::ostream& operator<< (std::ostream& out, const string& s)
	{
		//可以用范围for,也可以用迭代器   范围for是用宏写的,本质上也是迭代器!
		for (auto ch : s)
			out << ch;
		return out;
	}
	//重载>>
	std::istream& operator>> (std::istream& in, string& s)
	{
		//读取前要先清理掉原来存在的字符
		s.clear();
		//用get获取字符
		char ch = in.get();
		//先用一个数组存起来,再一起加
		char buff[128];
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			//原始方法,一个字符一个字符加太麻烦,先用一个数组存起来,再一起加
			//s += ch;
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				s += buff;
				i = 0;//重置i
			}
			ch = in.get();
		}
		//循环结束后可能还要一些字母没有存进去
		if (i != 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}

	//遍历方法的展示
	void Print(const string& s)
	{
	  //下标遍历
		for (size_t i = 0; i < s.size(); ++i)
			cout << s[i] << " ";
		cout << endl;
	  //迭代器遍历访问
		string::const_iterator it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	  //范围for
		for (auto &e : s)
			cout << e << " ";
		cout << endl;
	}

有新的后面再补充哦! 

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

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

相关文章

基于springboot+vue的医院药品管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

【Android】源码解析 Activity 的构成

本文是基于 Android 14 的源码解析。 当我们写 Activity 时会调用 setContentView() 方法来加载布局。现在来看看 setContentView() 方法是怎么实现的&#xff0c;源码如下所示&#xff1a; 路径&#xff1a;/frameworks/base/core/java/android/app/Activity.javapublic void…

Linux中服务端开发

1 创建socket,返回一个文件描述符lfd---socket(); 2 将lfd和IP&#xff0c;PROT进行绑定---bind(); 3 将lfd由主动变成被动监听---listen(); 4 接收一个新的连接&#xff0c;得到一个的文件描述符cfd--accept() --该文件描述符用于与客户端通信 5 while(1) { 接受数据&a…

【扩散模型系列3】DiT开源项目

文章目录 DiT原始项目Fast-DiT readmeSamplingTraining训练之前的准备训练DiTPyTorch 训练结果改进训练效果 Evaluation (FID, Inception Score, etc.) 总结 DiT原始项目 该项目仅针对DiT训练&#xff0c;并未包含VAE 的训练 项目地址 论文主页 Fast-DiT readme 该项目仅针…

性能优化篇(七) UI优化注意事项以及使用Sprite Atlas打包精灵图集

UI优化注意事项 1.尽量避免使用IMGUI(OnGUI)来做游戏时的UI&#xff0c;因为IMGUI的开销比较大。 2.如果一个UGUI的控件不需要进行射线检测&#xff0c;则可以取消勾选Raycast Target 3.尽量避免使用完全透明的图片和UI控件。因为即使完全透明&#xff0c;我们看不见它&#xf…

论文笔记:Code Llama: Open Foundation Models for Code

导语 Code Llama是开源模型Llama 2在代码领域的一个专有模型&#xff0c;作者通过在代码数据集上进行进一步训练得到了了适用于该领域的专有模型&#xff0c;并在测试基准中超过了同等参数规模的其他公开模型。 链接&#xff1a;https://arxiv.org/abs/2308.12950机构&#x…

[cg] Games 202 - NPR 非真实感渲染

NPR特性&#xff08;基于真实感渲染&#xff09; 真实感--》翻译成非真实感的过程 NPR风格 需要转换为渲染中的操作 1.描边 B-->普通边界&#xff08;不是下面几种的&#xff09; C-->折痕 M-->材质边界 S-->需要在物体外面一圈上&#xff0c;并且是多个面共享…

win11部署自己的privateGpt(2024-0304)

什么是privateGpt? privategpt开源项目地址 https://github.com/imartinez/privateGPT/tree/main 官方文档 https://docs.privategpt.dev/overview/welcome/welcome PrivateGPT是一个可投入生产的人工智能项目&#xff0c;利用大型语言模型&#xff08;LLMs&#xff09;的…

流行 NFT 的必备指南

​作者&#xff1a;stellafootprint.network 编译&#xff1a;mingfootprint.network 来源&#xff1a;Footprint Analytics Blog 随着爱好者们对 NFT 的兴趣不断高涨&#xff0c;Footprint Analytics 发布了一系列文章&#xff0c;重点介绍各种热门 NFT 系列。这些文章深入…

GBU808-ASEMI整流桥GBU808参数、封装、尺寸

编辑&#xff1a;ll GBU808-ASEMI整流桥GBU808参数、封装、尺寸 型号&#xff1a;GBU808 品牌&#xff1a;ASEMI 封装&#xff1a;GBU-4 最大重复峰值反向电压&#xff1a;800V 最大正向平均整流电流(Vdss)&#xff1a;8A 功率(Pd)&#xff1a;中小功率 芯片个数&#…

【网站项目】075学生信息管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

xss.haozi.me:0x0B

<svg><script>(1)</script>

WebSocket 详解教程

概述 WebSocket 是什么&#xff1f; WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。 WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 为什么需要 WebSocket &#xff1f; 了解计算机网络协议的人&#xff0c;应该都知道&#x…

基于云的虚拟桌面基础架构 (VDI)的优势有哪些?

OpenText™ Exceed TurboX™ &#xff08;ETX&#xff09; 长期以来一直是虚拟化在 Linux 主机上运行的图形要求苛刻的软件的黄金标准。ETX 最新版本&#xff08;12.5&#xff09;增加了许多Microsoft Windows功能&#xff0c;包括Windows服务器&#xff0c;使ETX成为任何Windo…

HCIA-Datacom题库(自己整理分类的)_33_DHCP协议多选【7道题】

1.使用动态主机配置协议DHCP分配IP地址有哪些优点? 可以实现IP地址重复利用 工作量大且不好管理 配置信息发生变化(如DNS),只需要管理员在DHCP服务器上修改,方便统一管理 避免IP地址冲突 2.网络中部署了一台DHCP服务器,但是管理员发现部分主机并没有正确获取到该DHCP服务…

Kafka面经

1.Kafka如何保证消息不丢失 生产者&#xff1a; 1.Producer 默认是异步发送消息&#xff0c;这种情况下要确保消息发送成功&#xff0c;有两个方法 a. 把异步发送改成同步发送&#xff0c;这样 producer 就能实时知道消息发送的结果。 b. 添加异步回调函数来监听消息发送的结…

UNIapp实现局域网内在线升级

首先是UNIapp 生成apk 用Hbuilder 进行打包 可以从网站https://www.yunedit.com/reg?gotocert 使用自有证书&#xff0c;目测比直接使用云证书要快一些。 发布apk 网站 用IIS发布即可 注意事项中记录如下内容 第一、需要在 iis 的MiMe 中添加apk 的格式&#xff0c;否则无法…

STM32(15)USART编程

使用USART实现STM32与电脑之间的通信 中介&#xff1a;USB转TTL模块 闭合总开关&#xff0c;外部时钟才会传输到分频器 c8t6手册里面写了&#xff0c;usart最大支持4.5MHz&#xff0c;所以选10 重映射时记得开启AFIO的时钟

Android logcat系统

一 .logcat命令介绍 android log系统: logcat介绍 : logcat是android中的一个命令行工具&#xff0c;可以用于得到程序的log信息. 二.C/Clogcat访问接口 Android系统中的C/C日志接口是通过宏来使用的。在system/core/include/android/log.h定义了日志的级别&#xff1a; /…

opencart3 添加速卖通商品脚本

非爬虫&#xff0c;只能把速卖通商品信息拿下来解析插入到自己的项目里。 刚接触opencart3没多久&#xff0c;有一些新项目需要添加商品&#xff0c;每次手动从速卖通复制信息又很慢&#xff0c;就自己写了一个脚本。 思路&#xff1a;速卖通商品详情页有一段数据包含了几乎所…