小言_互联网的博客

【C++】STL---string类的模拟实现

261人阅读  评论(0)

前言

  string类,想必大家都不陌生,这是一个用来管理字符串的类。让我们使用字符串时更方便,更遍历。所以今天我们就来简单的实现一下string类。


类的属性

我们要管理一个字符串,那么首先得有一个字符串。所以我们用char* 类型的指针,来指向一个块存储字符串的地址。_size则记录字符串的长度,_cacpcity 为字符串的空间容量

private:
		char* _str;
		size_t _size;
		size_t _cacpcity;

构造函数

构造函数

我们在实例化一个string对象的时候,可以string s("hello world"); 直接创建,也可以string s(); 使它初始时为空,所以我们可以用缺省的构造函数。如果不传参数,那么默认初始化为空。

	//缺省的构造函数 
		string(const char* str = "")
			: _size(strlen(str))
			,_cacpcity(_size)
		{
   
			//开辟一块内存
			_str = new char[_cacpcity + 1];//有效容量是cacpcity,要多一个用来存放\0
			strcpy(_str, str);
		}

析构函数

析构函数我们只需要释放 _str指向的空间即可。

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

拷贝构造函数

拷贝构造,就是拷贝一个字符串 例如:string s1("hello world"); string s2(s1); 。所以我们开辟一块与要拷贝字符串一样大小的空间,再把它一一复制给新字符串即可。

//拷贝构造
		string(const string& s)
			:_size(s._size)
			, _cacpcity(s._cacpcity)
		{
   
			//开辟一块和s一样的空间
			_str = new char[_cacpcity+1];
			strcpy(_str, s._str);
		}

操作符重载

赋值操作符重载

如果我们想用 = 操作符来给字符串赋值。那我们可以重载 = 操作符。

	//赋值操作符重载
		string& operator=(const string& s)
		{
   
			//如果不是自己给自己赋值
			if (this != &s)
			{
   
				//创建一块新空间
				char* tmp = new char[s._cacpcity+1];
				//拷贝
				strcpy(tmp, s._str);
				//销毁旧空间
				delete[] _str;
				_str = tmp;
				_size = s._size;
				_cacpcity = s._cacpcity;
			}
			return *this;
		}

 

当然也可以复用拷贝构造函数。

比较操作符重载

<重载

		//字符串比较函数重载
		bool operator<(const string& s)
		{
   
			return strcmp(_str, s._str) < 0;
		}

== 重载

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

<= 重载

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

!=重载

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

获取字符串长度

直接返回_size 即可

		//获取长度
		size_t size()
		{
   
			return _size;
		}

下标访问

字符串也可以进行下标访问,所以我们重载[]即可

		//下标访问
		char& operator[](size_t pos)
		{
   
			return _str[pos];
		}

当然,如果访问的是const修饰的字符串,那我们只能读,不能写。

	//只读
		const char& operator[](size_t pos) const
		{
   
			return _str[pos];
		}

迭代器

		//定义2个迭代器,一个是可读可写,一个是只读
		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;
		}

 

尾插一个字符

就是在字符串末端插入一个字符,我们只需要在_size的位置插入,随后_size++即可。不过要保证容量必须充足。

		//尾插一个字符
		void push_back(char c)
		{
   
			//检查容量
			if (_size == _cacpcity)
			{
   
				AddCacpcity(_cacpcity == 0 ? 15 : _cacpcity * 2);
			}
			_str[_size] = c;
			_size++;
			_str[_size] = '\0';
		}

扩容函数

void AddCacpcity(size_t newCacpcity)
		{
   
				//末尾位置留一个\0,所以+1
				char* str = new char[newCacpcity + 1];
				//旧字符串的内容拷贝到新字符串
				strcpy(str, _str);
				//销毁旧字符串
				delete[] _str;
				_str = str;
				_cacpcity = newCacpcity;
		}

追加字符串

如果想在尾部追加字符串的话,我们可以实现一个 append()函数。

		//追加一个字符串
		void append(const char* str)
		{
   
			//检查容量
			if (_cacpcity < (_size)+strlen(str))
			{
   
				AddCacpcity(_size + strlen(str));
			}
			//直接在末尾的位置加上str
			strcpy(_str + _size, str);
			_size += strlen(str);
		}

然后我们可以重载 +=运算符,拿来复用 append()函数。

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

		}

然后我们重载 +运算符,但需要注意的是,+运算符不改变当前字符串,所以我们要值返回。

		//+重载
		string operator+(const char* str)
		{
   
			string s(*this);
			s += str;

			return s;
		}

字符串清空

直接把第一个字符改成\0即可

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

字符串交换

我们用std命名空间里面的swap函数,对每个成员进行交换。

		//字符串交换
		void swap(string& s)
		{
   
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_cacpcity, s._cacpcity);
		}

以C的方式返回字符

c语言的字符串其实就是一个char*指针,遇到\0结束,所以我们直接返回str就可以了。

		//以c的方式返回字符串
		const char* c_str()const
		{
   
			return _str;
		}

判断字符串是否为空

直接判断第一个元素是否是 \0

		//判断字符串是否为空
		bool empty()const
		{
   
			return _str[0] == '\0';
		}

修改长度

修改长度我们要分多种情况,一般长度减少时,我们不改变现有容量。容量不够时,我们才增容。 如果 长度减少,或者不变,我们只需要把 减少的新长度给_size,然后把当前位置变成\0。如果是增加长度,我们要考虑容量,不够的话需要增容,然后memset函数把增容的部分改成你指定的字符(默认\0)。

		//修改长度
		void resize(size_t n, char c = '\0')
		{
   
			//如果修改的值比当前长度小
			if (n <= _size)
			{
   
				//截断
				_size = n;
				_str[_size] = '\0';
			}
			//如果修改的值比当前长度大
			else
			{
   
				//扩容
				if(n > _cacpcity)
				{
   
					AddCacpcity(n);
				}
				//从size位置到n的位置设置为c
				memset(_str + _size, c, n - _size);
				//最后位置填上\0
				_size = n;
				_str[_size] = '\0';
			}
		}

 

指定大小扩容

之前写过一个扩容函数,直接把指定的大小传过去即可。还是老规矩,减少不处理。

	//指定容量。只增加,减少不处理
		void reserve(size_t n)
		{
   
			if (n > _cacpcity)
			{
   
				AddCacpcity(n);
			}
		}

查找字符

该查找只会找到从指定位置开始,第一个出现的字符。如果要查找第二个,那么就在第一个字符的后面开始查找。

		// 返回c在string中第一次出现的位置
		size_t find(char c, size_t pos = 0) const
		{
   
			for (int i = pos; i < _size; i++)
			{
   
				if (_str[i] == c)
					return i;
			}
			
			return nops;
		}

nops是一个无符号的数,代表找不到返回的值。

static const size_t nops = -1;

查找子串

C语言中有strstr函数,我们可以复用。

		// 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos = 0) const
		{
   
			char* tmp = strstr(_str, s);
			if (tmp == NULL)
				return nops;
			
			return tmp - _str;
		}

指定位置插入字符

只需要把pos位置后面的字符都往后移动一格,随后把字符放进pos位置。

	// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		string& insert(size_t pos, char c)
		{
   
			//判断容量
			if (_size == _cacpcity)
			{
   
				AddCacpcity(_cacpcity == 0 ? 15 : _cacpcity * 2);
			}
			//pos位置后往后移
			size_t end = _size + 1;
			while (pos < end)
			{
   
				_str[end] = _str[end - 1];
				end--;
			}
			_str[pos] = c;
			_size++;

			return *this;
		}

 

指定位置插入字符串

插入字符是都往后移动一格,插入字符串那就是把pos位置后面的字符都像后 移动字符串的长度格。然后把字符串从pos位置开始写入。

//插入字符串
		string& insert(size_t pos, const char* str)
		{
   
			size_t len = strlen(str);
			//判断容量
			if (_cacpcity < (len + _size))
			{
   
				AddCacpcity(len + _size);
			}
			//移动len格
			size_t end1 = _size+1;
			size_t end2 = _size + len ;
			while (pos < end1 )
			{
   
				_str[end2] = _str[end1-1]  ;
				end1--;
				end2--;
			}
			int i = pos;
			while (*str)
			{
   
				 _str[i++] = *str++;
			}
			_size += len;
			return *this;
		}

 

删除指定区间

从pos位置开始,删除len个空间。那么我们需要先判断 len是否大于pos后面的长度,如果大于那就是后面全部删除,那么我们只需要把pos位置置空成\0即可。如果不大于就说明在中间删除,那么就从pos的第len个位置开始往pos后面的位置覆盖,覆盖到\0结束。

		// 删除
		string& erase(size_t pos, size_t len)
		{
   
			//如果要删除的长度大于后面的剩余长度
			if (len >= _size - pos)
			{
   
				len = _size - pos;
				_size -= len;
				_str[pos] = '\0';
				return *this;
			}
			//把后面的往前移,覆盖式删除
			size_t begin = pos+len;
			while (_str[begin])
			{
   
				_str[begin-len] = _str[begin];
				begin++;
			}
			_size -= len;
			return *this;
		}
		

 

全部代码:

namespace wyl
{
   
	class string
	{
   
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
		//缺省的构造函数 
		string(const char* str = "")
			: _size(strlen(str))
			,_cacpcity(_size)
		{
   
			//开辟一块内存
			_str = new char[_cacpcity + 1];
			strcpy(_str, str);
		}

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

		//拷贝构造
		string(const string& s)
			:_size(s._size)
			, _cacpcity(s._cacpcity)
		{
   
			//开辟一块和s一样的空间
			_str = new char[_cacpcity+1];
			strcpy(_str, s._str);
		}

		//赋值操作符重载
		string& operator=(const string& s)
		{
   
			//如果不是自己给自己赋值
			if (this != &s)
			{
   
				//创建一块新空间
				char* tmp = new char[s._cacpcity+1];
				//拷贝
				strcpy(tmp, s._str);
				//销毁旧空间
				delete[] _str;
				_str = tmp;
				_size = s._size;
				_cacpcity = s._cacpcity;
			}
			return *this;
		}

		//获取长度
		size_t size()
		{
   
			return _size;
		}
		
		//下标访问
		char& operator[](size_t pos)
		{
   
			return _str[pos];
		}
		//只读
		const char& operator[](size_t pos) const
		{
   
			return _str[pos];
		}

		//迭代器开始位置
		iterator begin()
		{
   
			return _str;
		}
		const_iterator begin() const
		{
   
			return _str;
		}

		//迭代器末尾位置
		iterator end()
		{
   
			return _str + _size;
		}

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

		// 扩容
		void AddCacpcity(size_t newCacpcity)
		{
   
				char* str = new char[newCacpcity + 1];
				strcpy(str, _str);
				delete[] _str;
				_str = str;
				_cacpcity = newCacpcity;
		}

		//尾插一个字符
		void push_back(char c)
		{
   
			//检查容量
			if (_size == _cacpcity)
			{
   
				AddCacpcity(_cacpcity == 0 ? 15 : _cacpcity * 2);
			}
			_str[_size] = c;
			_size++;
			_str[_size] = '\0';
		}


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

		//追加一个字符串
		void append(const char* str)
		{
   
			if (_cacpcity < (_size)+strlen(str))
			{
   
				AddCacpcity(_size + strlen(str));
			}
			strcpy(_str + _size, str);
			_size += strlen(str);
		}

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

		}

		//+重载
		string operator+(const char* str)
		{
   
			string s(*this);
			s += str;

			return s;
		}

		string operator+(const string& str)
		{
   
			string s(*this);
			s += str._str;

			return s;
		}


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

		//字符串交换
		void swap(string& s)
		{
   
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_cacpcity, s._cacpcity);
		}

		//以c的方式返回字符串
		const char* c_str()const
		{
   
			return _str;
		}

		//判断字符串是否为空
		bool empty()const
		{
   
			return _str[0] == '\0';
		}

		//修改长度
		void resize(size_t n, char c = '\0')
		{
   
			//如果修改的值比当前长度小
			if (n <= _size)
			{
   
				//截断
				_size = n;
				_str[_size] = '\0';
			}
			//如果修改的值比当前长度大
			else
			{
   
				//扩容
				if(n > _cacpcity)
				{
   
					AddCacpcity(n);
				}
				//从size位置到n的位置设置为c
				memset(_str + _size, c, n - _size);
				//最后位置填上\0
				_size = n;
				_str[_size] = '\0';
			}
		}

		//指定容量。只增加,减少不处理
		void reserve(size_t n)
		{
   
			if (n > _cacpcity)
			{
   
				AddCacpcity(n);
			}
		}

		//字符串比较函数重载
		bool operator<(const string& s)
		{
   
			return strcmp(_str, s._str) < 0;
		}

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

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

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

		// 返回c在string中第一次出现的位置
		size_t find(char c, size_t pos = 0) const
		{
   
			for (int i = pos; i < _size; i++)
			{
   
				if (_str[i] == c)
					return i;
			}
			
			return nops;
		}

		// 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos = 0) const
		{
   
			char* tmp = strstr(_str, s);
			if (tmp == NULL)
				return nops;
			
			return tmp - _str;
		}

		// 在pos位置上插入字符c/字符串str,并返回该字符的位置
		string& insert(size_t pos, char c)
		{
   
			//判断容量
			if (_size == _cacpcity)
			{
   
				AddCacpcity(_cacpcity == 0 ? 15 : _cacpcity * 2);
			}
			//pos位置后往后移
			size_t end = _size + 1;
			while (pos < end)
			{
   
				_str[end] = _str[end - 1];
				end--;
			}
			_str[pos] = c;
			_size++;

			return *this;
		}

		//插入字符串
		string& insert(size_t pos, const char* str)
		{
   
			size_t len = strlen(str);
			//判断容量
			if (_cacpcity < (len + _size))
			{
   
				AddCacpcity(len + _size);
			}
			//移动len格
			size_t end1 = _size+1;
			size_t end2 = _size + len ;
			while (pos < end1 )
			{
   
				_str[end2] = _str[end1-1]  ;
				end1--;
				end2--;
			}
			int i = pos;
			while (*str)
			{
   
				 _str[i++] = *str++;
			}
			_size += len;
			return *this;
		}



		// 删除
		string& erase(size_t pos, size_t len)
		{
   
			//如果要删除的长度大于后面的剩余长度
			if (len >= _size - pos)
			{
   
				len = _size - pos;
				_size -= len;
				_str[pos] = '\0';
				return *this;
			}
			//把后面的往前移,覆盖式删除
			size_t begin = pos+len;
			while (_str[begin])
			{
   
				_str[begin-len] = _str[begin];
				begin++;
			}
			_size -= len;
			return *this;
		}
		

	private:
		char* _str;
		size_t _size;
		size_t _cacpcity;

		static const size_t nops = -1;

	};
}

 

总结:

string类的细节还有很多,在这里只能简单实现一下。在实现的过程中需要注意的几点。

1.避免内存越界,否则析构时销毁会出错。
2.释放new出来的内存,避免内存泄漏。
3.需要深拷贝,否则会出现析构多次的情况。


转载:https://blog.csdn.net/Lin5200000/article/details/128519901
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场