The simulation implementation of the string class in STL [C++]

default member function

Constructor

The constructor is set as the default parameter, if no parameter is passed in, the default construction is an empty string. The initial size and capacity of the string are set to the length of the incoming C string (excluding '\0')

		//默认构造函数-全缺省
		string(const char * str ="")//常量字符串以\0为结束标志
		{
    
    
			_size = strlen(str);
			_capacity = strlen(str);//capacity存储有效字符,\0不是有效字符
				_str = new char [strlen(str) + 1];//strlen 计算字符不包含\0,但是需要多开一个空间给\0
				strcpy(_str, str);
		}

copy constructor

Before simulating the implementation of the copy constructor, we should first understand the deep and shallow copy:

Shallow copy: The memory space pointed to by the pointer of the copied target object and the pointer of the source object is the same space. Changes to one object will affect the other.
Deep copy: Deep copy means that the source object and the copy object are independent of each other. Changes to any one of these objects will not affect the other.

Deep copy writing method 1:

string(const string& s)
	:_str(new char[strlen(s._str) + 1]) //_str申请一块刚好可以容纳s._str的空间
	, _size(0)
	, _capacity(0)
{
    
    
	strcpy(_str, s._str);    //将s._str拷贝一份到_str
	_size = s._size;         //_size赋值
	_capacity = s._capacity; //_capacity赋值
}

First open up a space enough to accommodate the source object string, then copy the source object string to it, and then assign other member variables of the source object to it. Because the _str of the copied object and the _str of the source object do not point to the same space, the copied object and the source object are independent of each other.

Deep copy writing method 2 (recommended):

insert image description here

//第二种写法
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
    
    
	string tmp(s._str); //调用构造函数,构造出一个C字符串为s._str的对象
	swap(tmp); //交换这两个对象
}

The _str of the copied object and the _str of the source object do not point to the same space, and are independent of each other.

assignment operator overloaded function

Similar to the copy constructor, the analog implementation of the assignment operator overloading function also involves deep and shallow copying, and we also need to use deep copying. The following also provides several ways of writing deep copy:

		//第一种写法
		//s1=s3
		string& operator=(const string& s)
		//	string& operator=( string *this ,const string& s)
		{
    
    
			if (this!= &s)//不是自己给自己赋值 
			{
    
    
				//开辟一块和s3一样大的空间tmp,将s3的内容拷贝到tmp中,释放s1,s1的指针指向tmp
				char* tmp = new char[s._capacity + 1];//+1 是给\0的
				memcpy(tmp, s._str, s._size + 1);
				delete[] this->_str;
				this->_str = tmp;
				this->_size = s._size;
				this->_capacity = s._capacity;
			}
			return *this;
		}

This way of writing is to use the method of "passing by value" to receive the rvalue, let the compiler automatically call the copy constructor, and then we can exchange the copied object with the lvalue.


		//第2种写法(推荐)
		//s1=s3
		string& operator=(string tmp) //编译器接收右值的时候自动调用拷贝构造函数
			//string& operator=( string *this ,string tmp)
		{
    
    
			//this->swap(tmp) 
			swap(tmp);//s1和tmp交换
			return *this;
		}

However, the second way of writing cannot avoid assigning values ​​to yourself. Even if you assign values ​​​​to yourself, these operations will still be performed. Although the content of the string pointed to by _str in the object remains unchanged after the operation, the address of the string storage has changed. In order to avoid this kind of operation, we can use the following writing method:


		//第三种写法
		//s1=s3
		string& operator=(const string& s)
			//	string& operator=( string *this ,const string& s)
		{
    
    
			if (this != &s)//不是自己给自己赋值 
			{
    
    
				string tmp(s);
				//this->swap(tmp);//this就是s1
				swap(tmp);//s1和tmp交换,tmp指向s1 ,并且出了作用域,tmp就销毁了 
			}
			return *this;
		}

destructor

The destructor of the string class requires us to write, because the member _str in each string object points to a space in the heap area. When the object is destroyed, the corresponding space in the heap area will not be destroyed automatically. In order to avoid memory leaks, we You need to use delete to manually release the space in the heap area

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

begin

The role of the begin function is to return the address of the first character in the string

iterator begin()
{
    
    
	return _str; //返回字符串中第一个字符的地址
}
const_iterator begin()const
{
    
    
	return _str; //返回字符串中第一个字符的const地址
}

end

The function of the end function is to return the address of the character after the last character in the string (that is, the address of '\0')

iterator end()
{
    
    
	return _str + _size; //返回字符串中最后一个字符的后一个字符的地址
}
const_iterator end()const
{
    
    
	return _str + _size; //返回字符串中最后一个字符的后一个字符的const地址
}

The code for traversing strings with iterators is actually traversing strings with pointers

string s("hello world!!!");
string::iterator it = s.begin();
//auto it = s.begin();
while (it != s.end())
{
    
    
	cout << *it << " ";
	it++;
}
cout << endl;

Range for and iterators , when the code is compiled, the compiler will automatically replace the range for with the iterator form, that is to say, the bottom layer of the range for is the iterator, we have already implemented the iterator of the string class, and naturally it can also be used Range for traverses the string

string s("hello world!!!");
//编译器范围for将其替换为迭代器形式
for (auto e : s)
{
    
    
	cout << e << " ";
}
cout << endl;

size

The size function is used to obtain the current effective length of the string ( excluding '\0' )

//大小
size_t size()const
{
    
    
	return _size; //返回字符串当前的有效长度
}

capacity

The capacity function is used to get the current capacity of the string

//容量
size_t capacity()const
{
    
    
	return _capacity; //返回字符串当前的容量
}

reserve

1. When n is greater than the current capacity of the object, expand the capacity to n or greater than n.
2. When n is less than the current capacity of the object, do nothing.

void reserve(size_t n)//扩容
		{
    
    
			if (n > _capacity)
			{
    
    
				 char* tmp = new char [n + 1];//+1 是给\0开辟的空间
				 strcpy(tmp, _str);//将对象原本的C字符串拷贝过来(包括'\0')
				 delete[] _str;//释放对象原本的空间
				 _str = tmp;//将新开辟的空间交给_str
				 _capacity = n;
			}
		}

erase

insert image description here

		//版本一
		//void erase(size_t pos, size_t len = npos)
		//{
    
    
		   // assert(pos <= _size);
		   // //全部删完
		   // size_t n = _size - pos;//pos位置后面的字符
		   // if (len > n)
		   // {
    
    
		   //	 _str[pos] = '\0';//pos位置放\0
		   //	 _size = pos;
		   // }
		   // //删除一部分,n不完全删完
		   // else
		   // {
    
    
		   //	 //需要删除的部分数据 的后面几个覆盖需要删除的数据
		   //	 strcpy(_str + pos, _str + pos + len); //用需要保留的有效字符覆盖需要删除的有效字符
		   //	 _size -= len; //size更新
		   // }
		//}
		//版本二
		void erase(size_t pos, size_t len = npos)
		{
    
    
			assert(pos <= _size);
			//全部删完
			if (len == npos || pos + len >= _size)
			{
    
    
				_str[pos] = '\0';
				_size = pos;

			}
			//删除一部分,n不完全删完
			else
			{
    
    
				//需要删除的数据的 后几个数据 往前覆盖
				size_t end = pos + len;
				while (end <= _size)
				{
    
    
					_str[pos++] = _str[end++];
				}
				_size -= len;
			}
		}
		size_t find(char ch, size_t pos = 0)
		{
    
    
			assert(pos < _size);
			//遍历string 
			for (size_t i = 0; i < _size; ++i)
			{
    
    
				if (_str[i] == ch)
				{
    
    
					return i;
				}
			}
			return npos;
		}
		size_t find(const char* str, size_t pos = 0)
		{
    
    
			assert(pos < _size);
			const char* ptr = strstr(_str + pos, str);
			if (ptr)//ptr!= nullptr
			{
    
    
				return ptr - _str;
			}
			return npos;
		}

resize

1. When n is greater than the current size, expand the size to n, and the expanded character is ch. If ch is not given, it defaults to '\0'.
2. When n is smaller than the current size, reduce the size to n.
insert image description here

//改变大小
void resize(size_t n, char ch = '\0')
{
    
    
	if (n <= _size) //n<_size
	{
    
    
		_size = n; //将size调整为n
		_str[_size] = '\0'; //在size个字符后放上'\0'
	}
	else  //n>_size
	{
    
    
	   
		if (n > _capacity) //判断是否需要扩容
		{
    
    
			reserve(n); //扩容
		}
		//n>_size && n<_capacity
		for (size_t i = _size; i < n; i++) //将size扩大到n,扩大的字符为ch
		{
    
    
			_str[i] = ch;
		}
		_size = n; //size更新
		_str[_size] = '\0'; //字符串后面放上'\0'
	}
}

push_back

The push_back function is to insert a character at the end of the current string. Before inserting the end, it needs to determine whether it needs to increase the capacity. If necessary, call the reserve function to increase the capacity, and then insert the character at the end. Note that after inserting the character, it needs to be added to the character. Back setting on '\0'

void push_back(char ch )
		{
    
    
			//2倍扩容
			if (_size == _capacity)
			{
    
    
				reserve(_capacity == 0 ? 4 : _capacity * 2); 
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';//字符串后面放上'\0'
		}

append

The append function appends a string to the back of the current string. Before appending, it is necessary to judge the relationship between _size + len and _capacity to determine whether it needs to be increased, and then append the string to be appended to the back of the object, because The string to be appended has '\0' behind it, so we don't need to set '\0' behind it

void append(const char* str)
		{
    
    
			//至少扩容到_size+len
		size_t	len = strlen(str);
			if (_size + len > _capacity)//_size+len<=_capacity都不需要扩容
			{
    
    
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
		}

operator+=

//+=运算符重载
string& operator+=(char ch)
 //string & operator+= (string *this , const char * str) 
{
    
    
	push_back(ch); //尾插字符串
	return *this; //返回左值(支持连续+=)
}

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

insert

The function of the insert function is to insert characters or strings at any position in the string.
When the insert function is used to insert characters, it is first necessary to judge the legality of pos. If it is not legal, the operation cannot be performed. Then it is necessary to judge whether the current object can accommodate the string after the inserted character. If not, it is necessary to call the reserve function to expand the capacity. . The process of inserting characters is also relatively simple. First, move the position of pos and the characters behind it one bit backward to make room for the character to be inserted, and then insert the character into the string.

	 void insert(size_t pos, size_t n, char ch)//插入字符
		 {
    
    
			 assert(pos <= _size);
			 //扩容 
			 if (n + _size > _capacity)
			 {
    
    
				 //至少扩容到_size+n
				 reserve(_size+n);
			 }
			 挪动数据 版本一
			 //int end = _size;
			 //while (end>=(int)pos)  //int 和 size_t 在比较中涉及到算术转换,int需要转换为unsigned int 
			 //{
    
    
				// _str[end + n] = _str[end];
				// end--;
			 //}
			 //挪动数据 版本二
			 size_t  end = _size;
			 while (end >= pos && end!=npos )  //end是size_t ,end会等于-1 ,加了end!=npos,end就不会等于-1了
			 {
    
    
				 _str[end + n] = _str[end];
				 end--;
			 }
			 //插入数据 
			 for (size_t i = 0; i < n; i++)
			 {
    
    
				 _str[pos+i] = ch;

			 }
			 _size += n;
		 }
		 
		 void insert(size_t pos, const char* str)//插入字符串
		 {
    
    
			 assert(pos <= _size);
			 //扩容
			 size_t len = strlen(str);
			 if (_size + len >= _capacity)
			 {
    
    
				 reserve(_size + len);
			 }
			 //挪动数据 
			 int end = _size;
			 while (end>=(int)pos)
			 {
    
    
				 _str[end + len] = _str[end];
				 --end;
			 }
			 //插入数据 
			 for (size_t i = 0; i < len; i++)
			 {
    
    
				 _str[pos + i] = str[i];
			 }
			 _size += len;
		 }

swap

The swap function is used to exchange the data of two objects, just call the swap template function in the library directly to exchange each member variable of the object. But if we want to call the swap template function in the library here, we need to add "std::" before the swap function to tell the compiler to look for the swap function in the C++ standard library, otherwise the compiler will think that you are calling the Implemented swap function (proximity principle).

//交换两个对象的数据
void swap(string& s)
{
    
    
	//调用库里的swap
	::swap(_str, s._str); //交换两个对象的C字符串
	::swap(_size, s._size); //交换两个对象的大小
	::swap(_capacity, s._capacity); //交换两个对象的容量
}

substr

Start at position pos in str, intercept n characters, and return them
insert image description here

		string substr(size_t pos = 0, size_t len = npos)
		{
    
    
			assert(pos < _size);
			size_t n = len;
			//子串的长度大于源字符串,源字符串有多大,子串就有多大
			if (len == npos || pos + len > _size)
			{
    
    
				n = _size - pos;
			}
			//子串的长度小于源字符串,将源字符串和子串匹配的内容拷贝到tmp中
			string tmp;
			tmp.reserve(n);
			for (size_t i = pos; i < pos + n; ++i)
			{
    
    
				tmp += _str[i];
			}
			return tmp;
		}

c_str

The c_str function is used to obtain a character string in C form, and it can directly return the member variable _str of the object during implementation.
c_str converts a string object to a C-type string in order to interact with functions in the C language or functions that require C-style strings as parameters.

//返回C类型的字符串
	const char* c_str() const //将string转换为C类型的字符串,以便与C语言代码进行交互
		{
    
    
			return _str;
		}
void Test_string10()
{
    
    
	cxq::string  s1("hello world");
	s1 += '\0';
	s1 += "!!!!!!";//hello world\0!!!!!!\0
	cout << s1.c_str() << endl;//c形式的字符串遇到\0就终止,打印的结果就是hello world\0
	cout << s1 << endl;//打印_size个数的字符,而不是遇到\0就终止

}

If you use cout << s1.c_str() << endl, the c-form string will terminate when it encounters the first \0, and the printed result is hello world\0,
but if you use cout << s1 << endl, the printed result is _size number of characters in s1, instead of terminating when \0 is encountered

Summary:
The character array of c is terminated by \0 to calculate the length
string does not look at \0, and the length is calculated to terminate with _size

operator[ ]

The [ ] operator is overloaded so that the string object can obtain the character at the corresponding position of the string through [ ] + subscript like a C string

        char & operator[](size_t pos) //可读可写
			// char & operator[]( char *this ,size_t pos )
		 {
    
    
			 assert(pos <_size );
			 return _str[pos];//出了作用域,对象还在
		 }
		const  char& operator[](size_t pos) const //只能读
			 // char & operator[]( char *this ,size_t pos )
		 {
    
    
			 assert(pos < _size);
			 return _str[pos];//出了作用域,对象还在
		 }

find

Find finds a character or string in a string, that is, searches backward from the beginning of the string

      //正向查找第一个匹配的字符
		 size_t find(char ch, size_t pos = 0)
		 {
    
    
			 assert(pos < _size);
			 //遍历string 
			 for (size_t i = 0; i < _size; ++i)
			 {
    
    
				 if (_str[i] == ch)
				 {
    
    
					 return i;
				 }
			 }
			 //没有找到就返回npos
			 return npos;//npos是string类的一个静态成员变量,其值为整型最大值
		 }
		 //正向查找第一个匹配的字符串
		 size_t find(const char* str, size_t pos = 0)
		 {
    
    
			 assert(pos < _size);
			 const char* ptr = strstr(_str + pos, str);//strstr函数若是找到了目标字符串会返回字符串的起始位置,若是没有找到会返回一个空指针
			 if (ptr )//ptr!= nullptr
			 {
    
    
				 return ptr-_str;//计算目标字符串的起始位置和对象C字符串的起始位置的差值,进而得到目标字符串起始位置的下标
			 }
			 return npos;
		 }

clear

The clear function is used to empty the string stored in the object. When implementing, directly empty the _size of the object, and then put '\0' after the string

//清空字符串
void clear()
{
    
    
	_size = 0; //size置空
	_str[_size] = '\0'; //字符串后面放上'\0'
}

getline

The getline function is used to read a line of strings containing spaces. The implementation is basically the same as the overloading of the >> operator, except that it stops reading characters when it reads '\n'

		//读取一行含有空格的字符串
		istream& getline(istream& in, string& s)
		{
    
    
			s.clear();
			//分割多个string用空格或者换行,getline可以读取空格
			char ch =in.get();//读取第一个字符
			while (ch == '\n')
			{
    
    
				s += ch;将读取到的字符尾插到字符串后面
				ch = in.get(); //继续读取字符
			}
		}

Overloading of the >> operator

The purpose of overloading the >> operator is to allow the string object to be directly input using the >> operator like a built-in type.
Before inputting, we need to empty the C string of the object, and then read characters from the standard input stream, and stop reading until ' ' or '\n' is read.

	istream& operator>> (istream& in, string& s)//流提取
	{
    
    
		//get函数,无论什么字符都能从流中读取,包括换行和空格
		char ch = in.get();//从输入流中读取一个字符,并将其赋值给变量ch


		流提取多个string用空格或者换行分割
		// 处理前缓冲区前面的空格或者换行
		while (ch != ' ' && ch != '\n')
		{
    
    
			s += ch;
			ch = in.get();
		}
		return in;
	}

Overloading of the << operator

The purpose of overloading the << operator is to allow the string object to use the << operator to print directly like a built-in type

	ostream& operator<< (ostream& out, const string& str) //流插入
	{
    
    
		/*	for (size_t i = 0; i < str.size(); ++i)
			{
				out << str[i];
			 }*/
		for (auto ch : str)
		{
    
    
			out << ch;
		}
		return out;
	}

Explanation: In order to prevent library conflicts, the entire simulation implementation code written in the
namespace defined by oneself is written under the .h file. It is not allowed to put the declaration in .h and the definition in .cpp (it is not possible to separate the declaration and definition).
Because templates don't support separate compilation

#define  _CRT_SECURE_NO_WARNINGS 1
#pragma once //防止头文件被多次包含
#include<assert.h>
#include<iostream>
#include<string>
namespace cxq
{
    
    
	class string
	{
    
    
	public:
		typedef char* iterator;//可读可写
		typedef const char* const_iterator;//只能读不能写

		iterator begin()
		{
    
    
			return  _str;
		}
		iterator end()
		{
    
    
			return _str + _size;
		}
		const_iterator begin()  const
		{
    
    
			return _str;
		}
		const_iterator end() const
		{
    
    
			return _str + _size;
		}
		//默认构造函数-无参
		//string()
		//	:_size(0)
		//	,_capacity(0)
		//	,_str(new char [1]) // 如果是_str(nullptr)这种写法,会导致nullptr解引用
		//{
    
    
		//	_str[0] = '\0';//开一个空间放\0,
		//}
		//默认构造函数-全缺省
		string(const char* str = "")//常量字符串以\0为结束标志
		{
    
    
			_size = strlen(str);
			_capacity = strlen(str);//capacity存储有效字符,\0不是有效字符
			_str = new char[strlen(str) + 1];//strlen 计算字符不包含\0,但是需要多开一个空间给\0
		/*	strcpy(_str, str);*/
			memcpy(_str, str, _size + 1);
		}

		//string(const  char * str )
		//	:_size(strlen(str))
		//	,_capacity(strlen(str))//capacity存储有效字符,\0不是有效字符
		//	, _str(new char [_capacity +1] ) //strlen 计算字符不包含\0,但是需要多开一个空间给\0
		//{
    
    
		//	strcpy(_str, str);
		//}
		//拷贝构造
		string(const string& s)
		{
    
    
			_str= new char[s._capacity + 1]; //+1 给\0开辟空间
			 //拷贝
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}
		//第一种写法
		//string& operator=(const string& s)
			string& operator=( string *this ,const string& s)
		//	//s1=s3
		//	
		//{
    
    
		//	
		//	if (this!= &s)//不是自己给自己赋值 
		//	{
    
    
		//		//开辟一块和s3一样大的空间tmp,将s3的内容拷贝到tmp中,释放s1,s1的指针指向tmp
		//		char* tmp = new char[s._capacity + 1];//+1 是给\0的
		//		memcpy(tmp, s._str, s._size + 1);
		//		delete[] this->_str;
		//		this->_str = tmp;
		//		this->_size = s._size;
		//		this->_capacity = s._capacity;
		//	}
		//	return *this;
		//}

		第二种写法
		//string& operator=(const string& s)
		//	//	string& operator=( string *this ,const string& s)
		//		//s1=s3
		//{
    
    
		//	if (this != &s)//不是自己给自己赋值 
		//	{
    
    
		//		string tmp(s);
		//		//this->swap(tmp);//s1和tmp交换,tmp指向s1 ,并且出了作用域,tmp就销毁了 
		//		swap(tmp);//s1和tmp交换,tmp指向s1 ,并且出了作用域,tmp就销毁了 

		//	}
		//	return *this;
		//}


		//第三种写法(推荐)
		string& operator=(string tmp) //s1=s3
			//string& operator=( string *this ,string tmp)
		{
    
    
			//this->swap(tmp) 
			swap(tmp);//s1和tmp交换
			return *this;
		}
		~string()
		{
    
    
			delete[]_str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		const char* c_str() const //将string转换为C风格的字符串,以便与C语言代码进行交互
		{
    
    
			return _str;
		}
		size_t size()const
		{
    
    
			return _size;
		}
		char& operator[](size_t pos)
			// char & operator[]( char *this ,size_t pos )
		{
    
    
			assert(pos < _size);
			return _str[pos];//出了作用域,对象还在
		}
		const  char& operator[](size_t pos) const
			// char & operator[]( char *this ,size_t pos )
		{
    
    
			assert(pos < _size);
			return _str[pos];//出了作用域,对象还在
		}
		void reserve(size_t n)//扩容
		{
    
    
			if (n > _capacity)
			{
    
    
				cout << "reserve()->" << n << endl;
				char* tmp = new char[n + 1];//+1 是给\0开辟的空间
				memcpy(tmp, _str, _size + 1);
				delete[] _str;
				_str = tmp;//无法理解
				_capacity = n;
			}
		}
		void resize(size_t n, char ch = '\0')
		{
    
    
			//n<= _size
			if (n <= _size)
			{
    
    
				_size = n;
				_str[_size] = '\0';
			}
			//n>_size
			else


			{
    
    
				//判断是否需要扩容
				if (n > _capacity)
				{
    
    
					reserve(n);

				}
				//n>_size && n<_capacity,不需要扩容
				//插入数据
				for (size_t i = _size; i < n; ++i)

				{
    
    

					_str[i] = ch;

				}
				_size = n;
				_str[_size] = '\0';
			}

		}
		void push_back(char ch)
		{
    
    
			//2倍扩容
			if (_size == _capacity)
			{
    
    
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
		}
		void append(const char* str)
		{
    
    
			//至少扩容到_size+len
			size_t	len = strlen(str);
			if (_size + len > _capacity)//_size+len<=_capacity都不需要扩容
			{
    
    
				reserve(_size + len);
			}
			/*strcpy(_str + _size, str);*/
			memcpy(_str + _size, str, len + 1);
			_size += len;
		}
		string& operator+= (const char* str)
			//string & operator+= (string *this , const char * str) 
		{
    
    
			append(str);
			return *this;
		}
		string& operator+= (const char ch)
			//string & operator( string * this ,const char ch) 
		{
    
    
			push_back(ch);
			return *this;
		}
		void insert(size_t pos, size_t n, char ch)//插入字符
		{
    
    
			assert(pos <= _size);
			//扩容 
			if (n + _size > _capacity)
			{
    
    
				//至少扩容到_size+n
				reserve(_size + n);
			}
			挪动数据 版本一
			//int end = _size;
			//while (end>=(int)pos)  //int 和 size_t 在比较中涉及到算术转换,int需要转换为unsigned int 
			//{
    
    
			   // _str[end + n] = _str[end];
			   // end--;
			//}
			//挪动数据 版本二
			size_t  end = _size;
			while (end >= pos && end != npos)  //end是size_t ,end会等于-1 ,加了end!=npos,end就不会等于-1了
			{
    
    
				_str[end + n] = _str[end];
				end--;
			}
			//插入数据 
			for (size_t i = 0; i < n; i++)
			{
    
    
				_str[pos + i] = ch;

			}
			_size += n;
		}

		void insert(size_t pos, const char* str)//插入字符串
		{
    
    
			assert(pos <= _size);
			//扩容
			size_t len = strlen(str);
			if (_size + len >= _capacity)
			{
    
    
				reserve(_size + len);
			}
			//挪动数据 
			int end = _size;
			while (end >= (int)pos)
			{
    
    
				_str[end + len] = _str[end];
				--end;
			}
			//插入数据 
			for (size_t i = 0; i < len; i++)
			{
    
    
				_str[pos + i] = str[i];
			}
			_size += len;
		}
		//版本一
		//void erase(size_t pos, size_t len = npos)
		//{
    
    
		   // assert(pos <= _size);
		   // //全部删完
		   // size_t n = _size - pos;//pos位置后面的字符
		   // if (len > n)
		   // {
    
    
		   //	 _str[pos] = '\0';//pos位置放\0
		   //	 _size = pos;
		   // }
		   // //删除一部分,n不完全删完
		   // else
		   // {
    
    
		   //	 //需要删除的部分数据 的后面几个覆盖需要删除的数据
		   //	 strcpy(_str + pos, _str + pos + len); //用需要保留的有效字符覆盖需要删除的有效字符
		   //	 _size -= len; //size更新
		   // }
		//}
		//版本二
		void erase(size_t pos, size_t len = npos)
		{
    
    
			assert(pos <= _size);
			//全部删完
			if (len == npos || pos + len >= _size)
			{
    
    
				_str[pos] = '\0';
				_size = pos;

			}
			//删除一部分,n不完全删完
			else
			{
    
    
				//需要删除的数据的 后几个数据 往前覆盖
				size_t end = pos + len;
				while (end <= _size)
				{
    
    
					_str[pos++] = _str[end++];
				}
				_size -= len;
			}
		}
		size_t find(char ch, size_t pos = 0)
		{
    
    
			assert(pos < _size);
			//遍历string 
			for (size_t i = 0; i < _size; ++i)
			{
    
    
				if (_str[i] == ch)
				{
    
    
					return i;
				}
			}
			return npos;
		}
		size_t find(const char* str, size_t pos = 0)
		{
    
    
			assert(pos < _size);
			const char* ptr = strstr(_str + pos, str);
			if (ptr)//ptr!= nullptr
			{
    
    
				return ptr - _str;
			}
			return npos;
		}
		string substr(size_t pos = 0, size_t len = npos)
			//在str中从pos位置开始,截取n个字符,然后将其返回
		{
    
    
			assert(pos < _size);
			size_t n = len;
			//子串的长度大于源字符串,源字符串有多大,子串就有多大
			if (len == npos || pos + len > _size)
			{
    
    
				n = _size - pos;
			}
			//子串的长度小于源字符串,将源字符串和子串匹配的内容拷贝到tmp中
			string tmp;
			tmp.reserve(n);
			for (size_t i = pos; i < pos + n; ++i)
			{
    
    
				tmp += _str[i];//拷贝数据
			}
			return tmp;
		}
		void swap(string & s )
			//void swap( void * this ,string& s)

		{
    
    
			//调用库里的swap
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
		//bool operator<(const string& s)
		//	//bool operator<( string * this ,const string& s)
		//{
    
    
		//	size_t i1 = 0;
		//	size_t i2 = 0;
		//	while (i1 < _size &&i2 < s._size ) //i1和i2不能超过对应数组的size
		//	{
    
    
		//		if (_str[i1]> s._str[i2] )
		//		{
    
    
		//			return false;
		//		}
		//		else	if (_str[i1] < s._str[i2])
		//		{
    
    
		//			return true;
		//		}
		//		else//相等继续走
		//		{
    
    
		//			i1++;
		//			i2++;
		//		}
		//	
		//	}
		//	
		//	if (i1 == _size && i2 != s._size) //i1走完第一个数组了,i2没有走完
		//	{
    
    
		//		// "hello" "helloxx" true
		//		return true;
		//	}
		//	else
		//	{
    
    
		//		// "hello" "hello"   false
		//	// "helloxx" "hello" false
		//		return false;
		//	}
		//	// 如果不考虑代码可读性,可以直接写成return   i1 == _size && i2 != s._size 
		//}

		//第二种版本
		bool operator<(const string& s) const 
		{
    
    

			//分三种情况
			// "hello" "hello"   false
	// "helloxx" "hello" false
	// "hello" "helloxx" true

            
			int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
		

			
			return ret == 0 ? _size : s._size;
		}


		bool operator==(const string& s) const
		//	bool operator==( string  const *this , const string& s) const
			
		{
    
    
			//两个数组的大小相等 
			return _size == s._size && memcpy(_str, s._str, _size) ==0;
		}
		bool operator<=(const string& s) const
			//bool operator<=(string  const * this , const string& s) 

		{
    
    
			return *this < s  || *this ==s ;
		}
		bool operator>(const string& s) const
			//bool operator>( string   const * this const string& s) 
		{
    
    
			return !(*this <= s);
		}
		bool operator>=(const string& s) const
			//bool operator>=(string  const  * this ,const string& s) 
		{
    
    
			return !(*this < s);
		}
		bool operator!=(const string& s) const
			//bool operator!=( string const  *this , const string& s) 
		{
    
    
			return !(*this == s);
		}
		void clear()
		{
    
    
			_size = 0;
			_str[_size] = '\0';
		}
		//读取一行含有空格的字符串
		istream& getline(istream& in, string& s)
		{
    
    
			s.clear();
			//分割多个string用空格或者换行,getline可以读取空格
			char ch =in.get();//读取第一个字符
			while (ch == '\n')
			{
    
    
				s += ch;//将读取到的字符尾插到字符串后面
				ch = in.get(); //继续读取字符
			}
			return in;
		}
	private:
		size_t _size;
		size_t _capacity;
		char* _str;
	public:
		static size_t npos;//声明 
	};
	size_t string::npos = -1;//静态成员变量必须在类外面定义 
	ostream& operator<< (ostream& out, const string& str) //流插入
	{
    
    
		/*	for (size_t i = 0; i < str.size(); ++i)
			{
				out << str[i];
			 }*/
		for (auto ch : str)
		{
    
    
			out << ch;
		}
		return out;
	}
	//istream& operator>> (istream& in, string& s)//流提取
	//{
    
    
	//	s.clear();//清空上一次的字符
	//	//get函数,无论什么字符都能从流中读取,包括换行和空格
	//	char ch = in.get();//从输入流中读取一个字符,并将其赋值给变量ch


	//	//流提取多个string用空格或者换行分割
	//	// 处理前缓冲区前面的空格或者换行
	//	while (ch != ' ' && ch != '\n')
	//	{
    
    
	//		s += ch;
	//		ch = in.get();
	//	}
	//	return in;
	//}
	
	istream& operator>> (istream& in, string& s)//流提取
	{
    
    
		s.clear();//清空上一次的字符

		//补充;get函数,无论什么字符都能从流中读取,包括换行和空格

		char ch = in.get();//从输入流中读取一个字符,并将其赋值给变量ch
		char buff[128];
		int i = 0;

		//补充:流提取多个string用空格或者换行分割

		while (ch != ' ' && ch != '\n')	// 处理前缓冲区前面的空格或者换行
		{
    
    
			//将字符存到buff数组中,再将buff存到s中
			buff[i++] = ch;
			if (i == 127)
			{
    
    
				buff[i] = '\0';//最后一个位置放\0
				s += buff;//将buff存到s对象中
				i = 0;//重置
			}
			ch = in.get();//读取下一个ch

		}
		//不够128个字符,开辟的空间有剩余
		if (i != 0)
		{
    
    
			buff[i] = '\0';//在有效字符后加\0
			//再将buff存到s中
			s += buff;
		}
		return in;
	}
};





If you think this article is helpful to you, you might as well move your fingers to like, collect and forward, and give Xi Ling a big attention. Every
support of yours will be transformed into the driving force for me to move forward! ! !

Guess you like

Origin blog.csdn.net/qq_73478334/article/details/131822441