[C++]STL之string的模拟实现

news2024/9/20 16:34:14

上一章我们对string的常见接口及使用进行了讲解,接下来我们将对一些常见的接口,包括构造函数,析构函数,运算符重载等等进行模拟实现.方便我们理解string接口实现的原理.

在讲解之前先说一下string的成员变量.

首先是字符串内容_str,再是字符串的大小_size,最后是字符串的总容量大小_capacity.

class string
	{
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};

目录

构造函数

析构函数

拷贝构造函数

operator=赋值运算符重载

c_str

operator[]

size()

capacity()

empty()

operator+=

扩容函数(reserve)

resize()

push_back()

append()

insert()

erase()

find()

substr()

比较大小函数


构造函数

缺省值是一个空串,再给_str开辟空间时要多开辟一个空间存储'\0'

开好了空间最后需要把内容拷贝到_str.

		string(const char* str = "")
		{
			//这里有个细节,就是先计算出_size大小,然后再直接把_size赋值给_capacity,省了一次strlen()的调用.
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

析构函数

完成对string类成员的资源清理,空间释放等一些操作.

        ~string()
		{
            //释放_str的空间,并将其指向的空间置为空
			delete[] _str;
			_str = nullptr;

			_size = _capacity = 0;
		}

拷贝构造函数

说到string的拷贝构造函数,这里一定会涉及到深浅拷贝问题.

所以在讲解它的拷贝构造函数之前必须先了解它

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,可能就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,但是其他的对象不知道该资源已经被释放了,以为资源还有效,所以他们会继续对这个资源进行访问。这时就出现了违法访问。深拷贝就是为了解决浅拷贝的问题

深拷贝:就是给自己重新开辟一块空间,并将数据拷贝到新开辟的空间中,如果一个类中涉及到资源的管理,其拷贝的构造函数,赋值运算符重载以及析构函数必须要显式给出。(就是要手动写,不能用编译器自动生成的)。一般这种情况都是按照深拷贝方式提供。

所以拷贝的时候,需要重新给_str开辟一块空间.

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

这里也用图浅浅的介绍一下浅拷贝和深拷贝的区别.

operator=赋值运算符重载

正如上一个所说,=赋值运算符也同样存在深浅拷贝的问题,所以也必须进行深拷贝.

它和拷贝构造的主要区别就是:拷贝构造是对象还没有初始化时进行拷贝,而赋值运算符重载是对一个已经存在的变量进行赋值.

当然同样这里也需要深拷贝

也有一些需要注意的问题:例如s1=s2.我们把s2赋值给s1后,那么原本的s1空间该怎么办呢?

我们的解决方案是:

把原本的s1空间释放掉,然后再开辟一块和s2大小相同的空间,再把内容从s2拷贝到s1

		//= 运算符重载
		string& operator=(const string& s)
		{
			//不能自己赋值给自己
			if (this != &s)
			{//先释放掉原本的空间
				delete[] _str;
				_str = new char[s._capacity + 1];
				strcpy(_str, s._str);
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}

c_str

c_str就是返回c语言风格的字符串,既返回char*类型字符串,返回字符串首地址即可.

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

为什么加const呢?

第一个const是为了使普通对象和const对象都可以调用这个函数,因为权限只可以缩小,不可以放大.

第二个const是保证函数体内的内容不会被改变,既this指针指向的内容无法被改变.

operator[]

实现[]重载,是指传过来一个下标index,返回它index下标所对应的值

目的是让字符串可以像数组一样访问每一个元素.

		char& operator[](size_t index)
		{
            //下标必须小于字符串总大小
			assert(index < _size);

			return _str[index];

		}

当然为了const对象也可以调用,我们可以再写一个const修饰的operator[].

		const char& operator[](size_t index) const
		{
			assert(index < _size);

			return _str[index];
		}

size()

写一个函数,直接返回_size即可

        size_t size() const
		{
			return _size;
		}

那可能会有人想问了:既然返回_size,那我们直接调用它这个成员不就行了,为什么还有套一层函数呢?

这是因为_size是被private修饰的,我们是不能直接访问私有成员的.

所以需要实现一个公有的函数间接访问_size.

capacity()

这个所注意的和size完全一致.

		size_t capacity() const
		{
			return _capacity;
		}
		

empty()

只需要判断当前的size是否等于0即可.

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

operator+=

这个重载运算符我们上一章讲过是可以插入字符或者插入字符串的,这里也分别复用了push_back和append(),这两个函数后面将模拟实现.

		string& operator+= (const char ch)
		{
			push_back(ch);

			return *this;
		}

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

			return *this;
		}

扩容函数(reserve)

调整容量大小到n

先new一个n+1的新空间,再把原来的数据拷贝到新空间中去,然后释放掉原来的空间,然后将capacity设置为n.

void reserve(size_t n)
		{
			//n应该大于之前的容量
			if (n > _capacity)
			{
				//先开辟大小为n+1的空间
				char* tmp = new char[n + 1];
				//将原来的数据拷贝到tmp
				strcpy(tmp, _str);
				//释放掉原来的数据
				delete[] _str;
				
				//将扩容后的数据重新赋给_str
				_str = tmp;
				_capacity = n;
			}
		}

画图来理解一下它 

 

resize()

resize会有以下两种情况:

1.若n < _size,既重新调整后的大小小于原来的大小,会发生数据截断,只保留前n个字符.

2.若n > _size,这里直接复用reserve即可

既如果n<_capacity,此时_capacity不发生变化,多出的空间用ch替代.

如果n>_capacity,此时_capacity需要扩容(1.5倍速度,不一定是n),直到最接近为止.

		void resize(size_t n, char ch = '\0')
		{
			if (n > _size)
			{
				//插入数据
                //reserve会和容量进行比较以及是否需要阔人
				reserve(n);
                //多余的字符用ch替代    
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
                //字符串结束
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				//删除数据
                 //直接将第n个数据改为'\0',这样相当于将后面的数据全部删除了.
				_str[n] = '\0';
				_size = n;
			}
		}

push_back()

push_back的作用是在原字符串后上拼接一个字符,首先我们现需要判断空间是否足够,如不够,则需要扩容,复用之前的reserve函数,再进行插入数据,最后加上'\0'.

当然还可以利用复用insert()函数进行插入,这个后面再实现.

		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
				_str[_size] = ch;
				++_size;
				_str[_size] = '/0';
			}
		}

还以复用insert这样插入,会使代码健壮性更强,更加简洁.

这个inser()函数后面会实现.

			insert(_size, ch);

append()

这个与push_back不同的是:push_back()只能插入一个字符,append()只可以插入一个字符串.

这里的问题就出现了,我们不知道追加的字符串长度,自然扩容的时候也不知道扩大到多少,是2倍还是3倍,所以这里要看插入的字符串的长度len,只要要让空间开到_size+len.

让空间满足最低的情况,能把所有的字符容纳下,最后利用strcpy将其数据拷贝过来即可.

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

当然同样可以复用insert函数.

    insert(_size, str);

下面就该说insert函数了.

insert()

insert也分为两种情况:插入一个字符或插入多个字符(字符串)

插入一个字符:方法类似于顺序表的插入

		string& 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;
			}
			_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)
			{
				_str[end] = _str[end - len];
				--end;
			}
            //再把利用strncpy把指定长度的字符串插入
			strncpy(_str + pos, str, len);
			_size = _size + len;

		}

说了插入就该说删除了.

erase()

这个函数也比较巧妙,首先输入两个参数:第一个参数是要开始删除的下标,第二个参数是要删除的长度.

首先第二个参数默认缺省值是npos,npos是一个非常大的数.

首先判断len是否等于npos或者当前位置+len是否大于总长度,若是,则直接将pos位置置为'\0',后面的元素也就相当于删除了

如果不是,则把pos+len之后的元素拷贝到pos位置之后,这样就相当于删除了pos~pos+len之间的这一段字符.再把_size-len,相当于是一个覆盖的过程.

		void 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;
			}
		}

find()

也是实现两个,利用strstr()函数来查找字符串.

1.如果查找一个字符

  如果找到,则直接返回字符所对应的下标pos,否则返回npos.

2.如果查找一个字符串

   对于这种情况,找到字符串后,我们需要返回第一个字符的下标,通过指针差值确定目标字符串的位置。

1.查找一个字符

思路很简单,就是利用循环

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

2.查找一个字符串

利用strstr函数,从第pos个位置开始查找,如果找到则返回目标字符串的首元素地址,若没有找到则返回空指针

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

			const char* ptr = strstr(_str + pos, sub);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

substr()

 这个函数实现比较简单,复用之前实现的+=即可

首先计算出实际要切割的长度realLen = len

如果pos+len>_size或者len == npos,则需要重新计算realLen = _size - pos

然后循环realLen次,创建一个string类型的sub变量,每次利用sub+=这个字符即可.


		string substr(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			size_t realLen = len;
			if (len == npos || pos + len > _size)
			{
				realLen = _size - pos;
			}
			string sub;
			for (size_t i = 0; i < realLen; i++)
			{
				sub += _str[pos + i];
			}

			return sub;
		}

比较大小函数

实现比较大小,只需要实现两个运算符重载即可:

1. > 或 <其中任意一个

2.==

剩下的>=、<=、!=等等复用即可.

实现> 或 < 时,利用strcmp比较函数即可.

		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)
		{
			return !(*this == s);
		}

 这样string的模拟实现基本就完成了,下面是总代码:

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

		string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		//拷贝构造 - - - 1
		string(const string& s)
			:_str(new char[s._capacity + 1])
			, _size(s._size)
			, _capacity(s._capacity)
		{
			strcpy(_str, s._str);
		}
		//拷贝构造 - - - 2
	/*	string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			swap(_str, tmp._str);
			swap(_size, tmp._size);
			swap(_capacity, tmp._capacity);
		}*/

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

		iterator end()
		{
			return _str + _size;
		}

		const_iterator begin() const
		{
			return _str;
		}

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

		/
		void push_back(char ch)
		{
			/*if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
				_str[_size] = ch;
				++_size;
				_str[_size] = '/0';
			}*/
			insert(_size, ch);
		}

	
		string& operator += (char ch)
		{
			push_back(ch);

			return *this;
		}

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

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

			return *this;
		}



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


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

		void swap(string& tmp)
		{
			::swap(_str, tmp._str);
			::swap(_size, tmp._size);
			::swap(_capacity, tmp._capacity);
		}

		const char* c_str() const
		{
			return _str;
		}
		//
		size_t size() const
		{
			return _size;
		}

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

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

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

				_str = tmp;
				_capacity = n;
			}
		}
		
		/
		char& operator[](size_t index)
		{
			assert(index < _size);

			return _str[index];

		}

		const char& operator[](size_t index) const
		{
			assert(index < _size);

			return _str[index];
		}

		//

		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)
		{
			return !(*this == s);
		}

		

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

			const char* ptr = strstr(_str + pos, sub);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		string& 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;
			}
			_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)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, str, len);
			_size = _size + len;

		}

		void 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;
			}
		}

	


		string substr(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			size_t realLen = len;
			if (len == npos || pos + len > _size)
			{
				realLen = _size - pos;
			}
			string sub;
			for (size_t i = 0; i < realLen; i++)
			{
				sub += _str[pos + i];
			}

			return sub;
		}


	private:
		char* _str;
		int _size;
		int _capacity;
		const static size_t npos = -1;
	};
}

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

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

相关文章

微信小程序picker组件遇到的问题以及解决办法

一、picker基本概念二、遇到的问题三、如何解决四、延伸五、效果图一、picker基本概念 先来看一下官方文档中picker的基本概念&#xff1a; 从底部弹起的滚动选择器&#xff0c;现支持三种选择器&#xff0c;通过mode来区分&#xff0c;分别是普通选择器&#xff0c;时间选择器…

Bochs下载安装

文章目录下载Bochs配置BochsBochs Bochs是一个x86硬件平台的开源模拟器。它可以模拟各种硬件的配置。Bochs模拟的是整个PC平台&#xff0c;包括I/O设备、内存和BIOS。更为有趣的是&#xff0c;甚至可以不使用PC硬件来运行Bochs。事实上&#xff0c;它可以在任何编译运行Bochs的…

【Unity3D编辑器扩展】Unity3D中实现Text的字体的替换

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 在开发中会遇到要将场景中的Text的字体全部替换的情况。 所以…

NetInside网络分析帮您解决系统性能问题(一)

前言 某大学信息中心负责人表示&#xff0c;有用户反馈&#xff0c;在通过VPN访问某一IP的80端口时连接时断时续。同时信息中心给到的信息是通过VPN&#xff1a;XXX.XXX.253.5访问IP地址XXX.XXX.130.200的80端口出现访问时断时续问题。 需要通过分析系统看一下实际情况&#…

云原生周刊 | 人类、机器人与 Kubernetes

近日 Grafana 官网发表了一篇博客介绍了 2022 年比较有意思、脑洞大开的一些 Grafana 使用案例&#xff0c;比如监控特斯拉 Model 3 的充电状态、OTA 更新状况等等。 海事技术供应商 Royal IHC 利用 Grafana 展示客户船队的关键性能指标&#xff0c;例如燃料消耗、服务时间、大…

Allegro174版本新功能介绍之打开坐标超链接功能

Allegro174版本新功能介绍之打开坐标超链接功能 Allegro在升级到174的时候默认打开时,报表中的坐标是不带超链接的,如下图 直接点击坐标,是无法自动跳转到坐标所在位置的 但是Allegro174是开放了打开超链接的功能的,具体操作如下 选择Setup选择User Preferences

【 Vue3 + Vite + setup语法糖 + Pinia + VueRouter + Element Plus 第一篇】(持续更新中)

【 Vue3 Vite setup语法糖 Pinia VueRouter Element Plus 第一篇】(持续更新中) 1.使用 Vite脚手架创建 Vue3 项目 终端输入命令 npm create vite 项目名选择 Vue项目并回车根据自己的爱好&#xff0c;选择配置即可 2. 开启 Network 访问地址 npm run dev后 提示 use -…

磨金石教育||商业插画的发展现状如何?学习插画可以月入过万吗?

商业插画是什么&#xff1f;现如今&#xff0c;商业插画已经在生活中随处可见。你买的所有带包装的产品&#xff0c;上面的各种有趣的产品插图&#xff0c;就是插画师做的产品插画。特别是一些零食类的产品&#xff0c;在包装箱上&#xff0c;我们常可以看到各种大眼睛拟人化的…

电脑出现0xc00000e9错误代码的解决方法

每当假期结束回来&#xff0c;经常发现Windows系统的电脑一段时间不开机&#xff0c;开机就出现0xc00000e9的错误代码。为什么明明没有任何操作却出现错误呢&#xff1f;驱动人生带大家一文了解。 出现0xc00000e9错误代码的原因 先来了解一下电脑出现0xc00000e9错误代码的主要…

数字孪生架构

很多同学对数字孪生特别感兴趣&#xff0c;经常有同学问我&#xff1a;数据孪生系统怎么做&#xff1f;有没有教程&#xff1f;除了Unity开发&#xff0c;开发数字孪生还需要掌握什么技能&#xff1f;有人介绍了一个数字孪生的外包&#xff0c;从来没做过&#xff0c;能不能接&…

Spring 中常用的几个工具类

AnnotatedElementUtils 类 获取某个类的某个方法上是否有标注注解&#xff0c;并可以通过其他 API 获取到这个类注解上的属性值&#xff0c;该工具类其他 API 下面截图可以查看。 public static boolean isBeanAnnotated(Method method) {return AnnotatedElementUtils.hasAn…

Redis 应用问题解决

缓存穿透 key 对应的数据在数据源并不存在&#xff0c;每次针对此key的请求从缓存中获取不到&#xff0c;请求会都压到数据源&#xff0c;从而可能压垮数据源。 解决方案 一个一定不存在的缓存及查询不到的数据&#xff0c;由于缓存是不命中时被动写的&#xff0c;并且处于容…

docker 19.03构建跨平台的镜像包并推送到私有仓库

默认的docker构建image镜像是不能跨平台的,如果需要构建跨平台的镜像,需要docker的版本在19.03版本以上,并开启buildx。以下为具体的步骤 版本:docker 19.03。 一.安装/开启 buildx 1.1.手动开启dockerx开关 docker 19.3 暂默认不开启dockerx,需要手动开启 vim /etc/pro…

Scala 数据结构-集合

文章目录Scala 数据结构-集合一、集合简介1、不可变集合继承图2、可变集合继承图二、数组1、不可变数组(1) 创建数组(2) 访问数组(3) 遍历数组(4) 添加元素Scala 数据结构-集合 一、集合简介 1&#xff09;Scala的集合有三大类&#xff1a;序列seq&#xff0c;集合Set&#x…

解决fstab丢失,重启系统变为只读模式

现象描述&#xff1a; 背景&#xff1a;openEuler20.03 在/etc/fstab文件丢失、重启系统后&#xff0c;系统变为只读模式 [rootlocalhost ~]# echo 111 > 1.txt -bash: 1.txt: Read-only file system 解决方法&#xff1a; 查看系统信息&#xff0c;确认挂载信息&#…

【C进阶】数据在内存中的存储

数据在内存中的存储前言一、数据类型介绍&#xff08;一&#xff09;基本概念&#xff08;二&#xff09;类型的基本归类1.整型家族2.浮点型家族3.构造类型4.指针类型5.空类型二、整形在内存中的存储&#xff08;一&#xff09;原码、反码、补码1.概念2.为什么内存中存的是补码…

android input 事件分发 --- 注册input

android input 事件分发 --- 注册input应用注册input事件应用注册input事件 应用如果要监听input的事件&#xff0c;那么肯定就存在一个注册监听input事件的过程&#xff0c;跟随着addView方法我们跟着走一下frameworks/base/core/java/android/view/WindowManagerImpl.java Ov…

Centos7 安装 MongoDB

使用docker安装Mongo 1、拉取镜像 注&#xff1a;需要科学上网 docker pull mongo [rootlocalhost ~]# docker pull mongo Using default tag: latest latest: Pulling from library/mongo 846c0b181fff: Pull complete ef773e84b43a: Pull complete 2bfad1efb664: Pull co…

LeetCode:14. 最长公共前缀

14. 最长公共前缀1&#xff09;题目2&#xff09;思路3&#xff09;代码4&#xff09;结果1&#xff09;题目 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 示例 1&#xff1a; 输入&#xff1a;strs [“flo…

python常用快捷键

一、编辑&#xff08;Editing&#xff09;Ctrl Space 基本的代码完成&#xff08;类、方法、属性&#xff09;Ctrl Alt Space 快速导入任意类Ctrl Shift Enter 语句完成Ctrl P 参数信息&#xff08;在方法中调用参数&#xff09;Ctrl Q 快速查看文档F1 外部文档Shift F…