string的模拟实现

Posted aaaaaaaWoLan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了string的模拟实现相关的知识,希望对你有一定的参考价值。

拷贝构造和赋值重载的传统写法vs现代写法

传统写法

//传统写法

		//拷贝构造
		string(const string& s1)
		{
			assert(s1._str);
			
			_size = _capacity = strlen(s1._str);
			_str = new char[_capacity + 1];
			strcpy(_str, s1._str);

		}

		
		//赋值重载
		string& operator= (const string& s1)
		{
			//深拷贝
			delete[]_str;
			
			_size = _capacity = strlen(s1._str);
			_str = new char[_capacity + 1];
			strcpy(_str, s1._str);

			return *this;
		}

现代写法:

//现代写法

		//拷贝构造
		string(const string& s1)
			:_str(nullptr)
		{
			assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝

			string tmp(s1._str);
			std::swap(_str, tmp._str);
			_size = _capacity = strlen(_str);

		}


		//赋值重载
		string& operator= (const string& s1)
		{
			string tmp(s1);
			std::swap(_str, tmp._str);
			_capacity = _size = strlen(_str);


			return *this;
		}

拷贝构造和赋值重载的交换

//实现直接交换类的swap,库里的swap也能完成,但要完成三次深拷贝,效率低下
		//系统提供的:
		/*	template <class T> void swap(T&a, T&b)
			{
				//string的拷贝,三次深拷贝
				T c(a);
				a = b;
				b = c;
			}
		*/

		//自己实现的
		void swap(string& s)
		{
			//swap使用后,在局部作用域会默认只使用一种参数类型的交换,所以要用::来表明它是全局的,不受局部限制
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}

拷贝构造和赋值重载的现代写法可以复用swap

//现代写法

		//拷贝构造
		string(const string& s1)
			:_str(nullptr)
		{
			assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝

			string tmp(s1._str);
			swap(tmp);

		}


		//赋值重载
		string& operator= (const string& s1)
		{
			string tmp(s1);
			swap(tmp);

			return *this;
		}

范围for

如果我们把迭代器的名称一改,由begin->Begin,end->End,范围for就实现不了了

所以说范围for的底层是由迭代器实现的

“增”的操作(从尾部添加)

字符:push_back

字符串:append

还有一个很好用的operator+=,可以复用上面两个函数

既然要添加字符,就不得不涉及到增容:reserve

reserve实现

	//reserve
		void reserve(size_t capacity)
		{
			if (capacity > _capacity)
			{
				char* tmp = new char[capacity + 1];
				strncpy(tmp, _str, _size + 1);//要用strncpy或memcpy拷贝,如果用strcpy,可能源字符串是"hello\\0\\0\\0\\0",这样就会导致拷贝的内容不一致

				delete[] _str;
				_str = tmp;

				_capacity = capacity;

			}
			
		}

既然有reserve,那么也得提到resize

resize的实现:

//resize
		 void resize(int n, char x = '\\0')
		{ 
			 //有三种情况:
			 //1.n <= _size,将n赋值给_size,_str[_size] = '\\0'
			 //2._size < n <= _capacity,循环将多余的位置设置为'\\0'
			 //3.n > _capacity,先reserve(n),再将多余的位置设置为'\\0'

			 if (n <= _size)
			 {
				 _size = n;
				 _str[_size] = '\\0';

			 }
			 else if (n <= _capacity)
			 {
				 int end = n;
				 while (end > _size)
				 {
					 _str[end] = '\\0';
					 end--;
				 }

				 _size = n;
			 }
			 else
			 {
				 reserve(n);

				 int end = n;
				 while (end > _size)
				 {
					 _str[end] = '\\0';
					 --end;
				 }

				 _size = n;
			 }
		}

“改”的实现:insert

特殊情况:pos和是size_t的类型且pos为0时

<<、>>及inline的重载

<<容易实现,>>的输入遇到空格和回车时会停止读取,所以采用循环读取字符的方式,当字符为空格或回车时,就停止输入,可以用cin.get()接口来实现字符的读取,类似getchar

getline的原理与>>类似,遇到回车才停止输入。

注意:>>和getline的输入是覆盖输入的,所以输入前需要用clear接口清空

总体实现:

#pragma once

#include<iostream>
#include<string.h>
#include<assert.h>

using namespace std;

namespace ysj{
	

	class string {
		
	public:
		typedef char* iterator;

		//*****************************************************************************************************************************
		//构造函数
		string(const char* str = "")//默认缺省值不给nullptr,因为下面要将str作为strlen的参数,会引发错误
		{
			_size = _capacity = strlen(str);//_capacity是指有效容量,不包括\\0,但实际开的空间要多一个字节容纳\\0
			_str = new char[_capacity + 1];
			strcpy(_str, str);

		}


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

		//传统写法

		//拷贝构造
		/*string(const string& s1)
		{
			assert(s1._str);
			
			_size = _capacity = strlen(s1._str);
			_str = new char[_capacity + 1];
			strcpy(_str, s1._str);

		}*/

		
		//赋值重载
		//string& operator= (const string& s1)
		//{
		//	//深拷贝
		//	delete[]_str;
		//	
		//	_size = _capacity = strlen(s1._str);
		//	_str = new char[_capacity + 1];
		//	strcpy(_str, s1._str);

		//	return *this;
		//}


		//现代写法

		//拷贝构造
		string(const string& s1)
			:_str(nullptr)
		{
			assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝

			//string tmp(s1._str);
			//std::swap(_str, tmp._str);
			//_size = _capacity = strlen(_str);

			string tmp(s1._str);
			swap(tmp);

		}


		//赋值重载
		string& operator= (const string& s1)
		{
			/*string tmp(s1);
			std::swap(_str, tmp._str);
			_capacity = _size = strlen(_str);*/

			string tmp(s1);
			swap(tmp);

			return *this;
		}

		//实现直接交换类的swap,库里的swap也能完成,但要完成三次深拷贝,效率低下
		/*	template <class T> void swap(T&a, T&b)
			{
				//string的拷贝,三次深拷贝
				T c(a);
				a = b;
				b = c;
			}
		*/

		
		void swap(string& s)
		{
			//swap使用后,在局部作用域会默认只使用一种参数类型的交换,所以要用::来表明它是全局的,不受局部限制
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}

		//*********************************************************************************************************************


		//*********************************************************************************************************************

		//查

		//1.[]重载
		//可读可写返回
		char& operator[](size_t pos)
		{
			assert(pos <= size());

			return _str[pos];
		}

		//只读返回
		char& operator[](size_t pos)const
		{
			assert(pos <= size());

			return _str[pos];
		}


		//2.迭代器
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

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

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


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

		//3.范围for,其本质就是用迭代器实现的

		//万一operatoe[]想以只读的方式返回,size()也要是const的
		size_t size()const
		{
			return _size;
		}
		//*********************************************************************************************************************


		//*********************************************************************************************************************


		//增
		//1.push_back
		void push_back(char x)
		{
			//判断增容,若需要增容,使用reserve增容
			if (_size == _capacity)
			{
				int capacity = (_capacity == 0 ? 4 : 2 * _capacity);
				reserve(capacity);
			}

			_str[_size] = x;
			_str[_size + 1] = '\\0';
			++_size;

		}


		//2.append
		void append(const char* str)
		{
			int len = strlen(str) + _size;

			if (len > _capacity)
			{
				reserve(len);
			}

			strncpy(_str + _size, str, strlen(str) + 1);
			_size = len;
			
			_str[_size] = '\\0';
		}

		//3.operator+=
		//可复用append和push_back的代码
		
		//字符
		string& operator+=(char x)
		{
			push_back(x);

			return *this;
		}

		//字符串
		string& operator+=(const char* str)
		{
			append(str);

			return *this;
		}

		//reserve
		void reserve(size_t capacity)
		{
			if (capacity > _capacity)
			{
				char* tmp = new char[capacity + 1];
				strncpy(tmp, _str, _size + 1);//要用strncpy或memcpy拷贝,如果用strcpy,可能源字符串是"hello\\0\\0\\0\\0",这样就会导致拷贝的内容不一致

				delete[] _str;
				_str = tmp;

				_capacity = capacity;

			}
			
		}

		//resize
		 void resize(int n, char x = '\\0')
		{ 
			 //有三种情况:
			 //1.n <= _size,将n赋值给_size,_str[_size] = '\\0'
			 //2._size < n <= _capacity,循环将多余的位置设置为'\\0'
			 //3.n > _capacity,先reserve(n),再将多余的位置设置为'\\0'

			 if (n <= _size)
			 {
				 _size = n;
				 _str[_size] = '\\0';

			 }
			 else if (n <= _capacity)
			 {
				 int end = n;
				 while (end > _size)
				 {
					 _str[end] = '\\0';
					 end--;
				 }

				 _size = n;
			 }
			 else
			 {
				 reserve(n);

				 int end = n;
				 while (end > _size)
				 {
					 _str[end] = '\\0';
					 --end;
				 }

				 _size = n;
			 }
		}


		//*********************************************************************************************************************



		//*********************************************************************************************************************
		 //改,insert,在pos之前插入
		 
		 //字符
		 void insert(char x, size_t pos)
		 {
			 assert(pos <= _size);

			 if (_size == _capacity)
			 {
				 reserve(2 * _capacity);
			 }

			 size_t end = _size + 1;//如end等于_size+1,当pos等于0时,下面的while循环中,end永远不会小于0,就陷入了死循环

			 while (end > pos)
			 {
				 _str[end] = _str[end - 1];
				 --end;
			 }
			 _str[pos] = x;

			 ++_size;
		 }

		 //字符串
		 void insert(const char* s, size_t pos)
		 {
			 assert(pos <= _size);

			 int len = strlen(s);

			 if (len + _size > _capacity)
			 {
				 reserve(len + _size);
			 }

			 size_t end = _size + 1;//如end等于_size+1,当pos等于0时,下面的while循环中,end永远不会小于0,就陷入了死循环

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

			 strncpy(_str + pos, s, strlen(s));

			 _size += len;
		 }



		//*********************************************************************************************************************



		//*********************************************************************************************************************
		 //删,erase,从pos位置开始删除
		 ///默认从开头删
		 string& erase(size_t pos = 0, size_t len = npos)//静态缺省变量npos,表示当没有给出删除的位置时,默认从npos开始,也就是字符串的末尾
		 {
			 assert(pos < _size);

			 if (len > _size - pos)//剩余字符长度小于删除字符长度
			 {
				 //将从pos位置开始的之后所有字符都删除
				 _str[pos] = '\\0';
				 _size = pos;
			 }
			 else//向前挪
			 {
				 strncpy(_str + pos, _str + pos + len, strlen(_str + pos + len) + 1);
				 _size -= len;
			 }

			 return *this;

		 }


		//*********************************************************************************************************************
		



		//*********************************************************************************************************************
		
		 //find、rfind的实现
		 //find

		 //字符
		 size_t find(char x, size_t pos = 0)//找到了返回下标,没找到返回npos
		 {
			 for (int i = pos; i < _size; ++i)
			 {
				 if (x == _str[i])
					 return i;
			 }

			 return npos;
		 }

		 //字符串
		 size_t find(const char*s, size_t pos = 0)
		 {
				char* ret = nullptr;
			 //用strstr查找
			 for (int i = pos; i < _size; ++i)
			 {
				 ret = strstr(_str + i, s);

				 if (ret)
				 {
					 return ret - _str;
				 }
			 }

			 return npos;
		 }

		 //refind

		 //字符
		 size_t rfind(char x,  size_t pos = npos)//默认从最后开始查找
		 {

			 if (pos >= _size)
			 {
				 pos = _size - 1;
				 

			 }

			 for (int i = pos; i >= 0; --i)
			 {
				 if (_str[i] == x)
				 {
					 return i;
				 }
			 }

			 return npos;


		 }

		 //字符串
		 size_t rfind(const char* s, size_t pos = npos)
		 {
			 if (pos >= _size)
			 {
				 pos = _size - 1;
			 }

			 char* ret = nullptr;
			 for (int i = pos; i >= 0; --i)
			 {
				 ret = strstr(_str + i, s);

				 if (ret)
				 {
					 return ret - _str;
				 }
			 }

			 return npos;
		 }



		 //*********************************************************************************************************************

		 //clear重载,清空字符串
		 void clear()
		 {
			 _size = 0;
			 _str[0] = '\\0';
		 }


		//*********************************************************************************************************************

		  //>的重载
		 bool operator>(const string& s)const
		 {
			 assert(s._str);
			 return strcmp(_str, s._str) > 0;
		 }



		 //==的重载
		 bool operator==(const string& s)<

以上是关于string的模拟实现的主要内容,如果未能解决你的问题,请参考以下文章

C++:Copy-On-Write技术以及string类的模拟实现

C++string容器模拟实现

模拟实现C++中string的常规操作

C++ 初阶string底层框架模拟实现

C++ 初阶string底层框架模拟实现

vue-toy: 200行代码模拟Vue实现