Detailed explanation of C++STL: Simulation Implementation of string

Deep and shallow copy of string

Before starting to simulate the implementation of the string class, let's talk about the deep and shallow copy of string

In the previous study of classes and objects, we know that there is no problem using shallow copy (value copy) for Date classes like Date, but for classes like string, shallow copy cannot be used, but deep copy must be used. Then why? Now let's talk about it

Shallow copy: also known as value copy, the compiler only copies the values in the object. The copied pointer of the target object and the pointer of the source object point to the same memory space. At this time, they will share the same resource. When an object is deleted, another object will be added or deleted. And a more serious problem is that when both objects are destroyed, the destructor will be called twice, and the destructor is the same space, resulting in the space being released twice, resulting in an error from the compiler.

Deep copy: the space pointed by the pointer of the target object and the pointer of the source object are independent of each other. There will be no problem of sharing the same resource, so there will be no problem of illegal access to memory

Next, let's show the deep copy and shallow copy through drawing and code

Shallow copy

       //Destructor
		~string()
		{
			delete[]_str;
			_str = nullptr;
		}
private:
		char* _str;

int main()
{
    string s1("hello");
    string s2(s1);
    return 0;
}

Deep copy

The copy construction of string has these problems, and the overload of assignment operator of string also has these problems. Therefore, the overload of assignment operator also needs to realize deep copy

For the deep copy of string copy structure, there are two ways of traditional writing and modern writing. The same is true for the overload of assignment operator. Let's take a look at it together.

Traditional writing
//Traditional writing
		//copy construction 
		//s1=s3
		string(const string& s)
		{
            //Apply for a space as big as you
			_str = new char[strlen(s._str) + 1];
            //Then copy the values in your space
			strcpy(_str, s._str);
		}
		//Assignment operator overload
		string& operator=(const string& s)
		{
			if (this != &s)
			{
			 //Free your space first
			 delete[]_str;
			 //Open a space as big as you
			 _str = new char[strlen(s._str) + 1];
			 //Then copy your value
			 strcpy(_str, s._str);
			}
			return *this;
		}

Modern writing

//Modern writing
		//copy construction 
		//This is like: I want to eat something, but I don't want to make it myself, so I order a takeout and ask the rider to bring it to me, and then there's some garbage in my house
		//I forgot to throw it out in the morning, but I don't want to throw it out. I'll ask the rider to throw it out for me. If I don't throw it out, I'll give you a bad comment
		string(const string& s)
		{
			//First, we have to_ str set to null pointer
			//Otherwise, temp and_ After str exchange, the destructor will be called out of the function
			//Illegal access to memory will occur
			_str = nullptr;
			string temp(s._str);
			swap(_str, temp._str);
		}

		//Assignment operator overload
		//s1 = s3
		string& operator=(string s)
		{
			swap(_str, s._str);
			return *this;
		}
        //The second way to write
        string& operator=(const string& s)
		{
			//Prevent this from happening
			//s1 = s1
			if (this != &s)
			{
				string temp(s);
				swap(_str, temp._str);
			}
			return *this;

After talking about the deep and shallow copy problem, let's simulate and implement the string class

Interface of each function of string class

namespace mlf
{
	//Simulate the implementation of string class
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		//Default member function
		string(const char* str = "");         //Constructor
		string(const string& s);              //copy constructor 
		string& operator=(const string& s);   //Assignment operator overloaded function
		~string();                            //Destructor

		//Iterator correlation function
		iterator begin();
		iterator end();
		const_iterator begin()const;
		const_iterator end()const;

		//Capacity and size correlation function
		size_t size()const;
		size_t capacity();
		void reserve(size_t n);
		void resize(size_t n, char ch = '\0');
		bool empty()const;

		//Modify string related functions
		void push_back(char ch);
		void append(const char* str);
		string& operator+=(char ch);
		string& operator+=(const char* str);
		string& insert(size_t pos, char ch);
		string& insert(size_t pos, const char* str);
		string& erase(size_t pos, size_t len = npos);
		void clear();
		void swap(string& s);
		char* c_str()const;

		//Access string related functions
		char& operator[](size_t i);
		const char& operator[](size_t i)const;
		size_t find(char ch, size_t pos = 0)const;
		size_t find(const char* str, size_t pos = 0)const;

	private:
		char* _str;       
		size_t _size;     //The current valid length of the record string
		size_t _capacity; //Record the current capacity of the string
		static const size_t npos; //Static member variable
	};
    //Static member variables should be initialized outside the class
	const size_t string::npos = -1;
    
        //Relational operator overloaded function
		bool operator>(const string& s1,const string& s2)const;
		bool operator>=(const string& s1,const string& s2)const;
		bool operator<(const string& s1,const string& s2)const;
		bool operator<=(const string& s1,const string& s2)const;
		bool operator==(const string& s1,const string& s2)const;
		bool operator!=(const string& s1,const string& s2)const;

	//< < and > > operator overloaded function
	istream& operator>>(istream& in, string& s);
	ostream& operator<<(ostream& out, const string& s);
	istream& getline(istream& in, string& s);
}

Note: in order to prevent naming conflicts with the string class in the standard library, the simulation implementation needs to be placed in its own namespace. At this time, the namespace will play its role.

Default member function

Constructor

It should be noted that the default value here cannot be given to nullptr, because if it is nullptr, strlen(nullptr) will dereference nullptr and the program will crash. Therefore, our default value is an empty string, and we will_ Capacity and_ Size gives the same value, so you need to give a total of '\ 0' when opening space. Of course, you can also choose not to give the same value of capacity and size as we do. The specific implementation depends on your own situation. After opening the same space as str string, we will copy the C string of str to the opened space.

//Constructor
		string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			//Make room for storing strings. Give '\ 0' the extra one
			_str = new char[_capacity + 1];
			//Copy the C string to the opened space
			strcpy(_str, str);
		}
copy constructor

Note: str here needs to be given an initial value, which is generally set to nullptr. If you don't give it, there will be problems. Why? Because if str does not give the initial value, it is a random value, pointing to a random space. After exchanging with the temp object, the scope is out, and temp calls its destructor. At this time, it releases a random space, resulting in program errors.

        //copy construction 
		//s1(s3)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string temp(s._str);
			/*swap(_str, temp._str);
			swap(_size, temp._size);
			swap(_capaticy, temp._capaticy);*/
			swap(temp);
		}
Assignment operator overload
        //Assignment operator overload
		//s1 = s3
		string& operator=(string s)
		{
			/*swap(_str, s._str);
			swap(_size, s._size);
			swap(_capaticy, s._capaticy);*/
			swap(s);//Exchange str, size, capacity of two objects

			return *this;//Support continuous assignment
		}

It should be noted that the above writing method cannot avoid assigning values to yourself, but generally speaking, you will not assign values to yourself. However, in order to avoid assigning values to yourself, you can write as follows:

        string& operator=(const string& s)
		{
			//Prevent this from happening
			//s1 = s1
			if (this != &s)
			{
				string temp(s);//Using s object copy to construct temp
				swap(temp);//Swap these two objects
			}
			return *this;//Support continuous assignment

Note: the simulation of swap member function is implemented later

Destructor
        //Destructor
		~string()
		{
			delete[]_str;//Free the space pointed to by str
			_str = nullptr;//Set str to null to prevent it from becoming a wild pointer
			_size = 0;//Set the effective number to 0
			_capacity = 0;//Set the space size to 0
		}

Let's talk about the small details of implementing interface functions:

  • For read-only interface functions, we only implement the version with const
  • For write only interface functions, we only implement the version without const
  • For readable and writable interface functions, we need to implement two versions, one with const and the other without const.

Therefore, whether to add const depends on the functionality of the interface

Iterator related functions

The underlying iterator in our string class is actually implemented with pointers, but it should be noted that not all iterators are pointers. Since the iterator can read and modify the contents of the string when accessing the string, we need to implement two versions of iterators, one that only supports reading the string and the other that supports both reading and modifying the string.

        typedef char* iterator;
		typedef const char* const_iterator;
		
begin

The begin member function returns the address of the first character in a string

        //Readable and writable
		iterator begin()
		{
			return _str;//Returns the address of the first character in a string
		}
        
        //read-only
		const_iterator begin()const
		{
			return _str;//Returns the address of the first character in a string
		}
end

The end member function returns the address of the last character in the string (the address of '\ 0'). Note: '\ 0' is not a valid character. It is a character used to identify the end of the string.

        //Readable and writable
        iterator end()
		{
			return _str + _size;//Returns the address of the last character in a string (the address of '\ 0')
		}

        //read-only
        const_iterator end()const
		{
			return _str + _size;//Returns the address of the last character in a string (the address of '\ 0')
		}

Functions related to capacity and size

The member variable permissions of the string class object we simulated are private, and we can't directly access them outside the class. Then if we want to access size and capacity, we can implement two member functions ourselves to obtain the size and capacity of the string object by calling the member function.

size

The size member function is used to obtain the effective length of the current string (excluding '\ 0', which is not a valid character)

        size_t size()const
		{
			return _size;//Returns the valid length of the current string
		}
capacity

The capacity member function is used to obtain the capacity of the current string

        size_t capacity()const
		{
			return _capacity;//Returns the capacity of the current string
		}
empty

The empty member function is used to determine whether the current string is empty

        //Judge whether the current string is empty
		bool empty()const
		{
			return strcmp(_str, "") == 0;
		}
reserve

When implementing the reserve function, we must first understand its rules:

1. When n is greater than the space capacity of the current object, it is necessary to expand the space to N or greater than n

2. When n is less than the space capacity of the current object, the space capacity does not change.

		// Open space and expand capacity
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				//Open n+1 spaces and one for '\ 0'
				char* temp = new char[n + 1];
				//Copy the original data to temp
				//Copy all the data in the source string
				strncpy(temp, _str,_size+1);
				//Release_ str
				delete[]_str;
				//Let again_ str points to temp
				_str = temp;
				//Then update capacity
				_capacity = n;
			}
		}

Note: we need to use strncpy to copy the data of the source string to temp instead of strcpy. This is to prevent the source string from containing the valid character '\ 0'. In order to copy it, the random value will appear in the string when the character or string is inserted next time (because the valid character '\ 0' is lost), If the source string contains multiple '\ 0', strcpy will end the copy after copying the first '\ 0', so we can't use strcpy.

resize

Similarly, when implementing the resize function, we also have to find out its rules:

1. When n is less than size, the effective length (size) of the string becomes n

2. When n is greater than size but less than capacity, we need to add the character ch after the current string. If ch is not given, it defaults to '\ 0' until the size is equal to N, assign '\ 0' to the position where the subscript of the string is n, and then update the value of size. If n is greater than capacity, we need to increase capacity first, and then perform the following operations.

		// Open space + initialize, expand capacity and initialize space. I want to move, too
		void resize(size_t n,char ch = '\0')
		{
			//If n is less than_ size
			if (n < _size)
			{
				//Then the effective number is n
				_size = n;
				_str[_size] = '\0';
			}
			//n greater than_ size
			else
			{
				//If n is greater than capacity
				//We'll add capacity
				if (n>_capacity)
				{
					reserve(n);
				}
					begin point'\0'Location of
					//char* start = _str + _size;
					//char* end = _str + n;
					//while (start != end)
					//{
					//	*start++ = ch;
					//	++_size;
					//}
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
					_str[n] = '\0';
					_size = n;
			}
		}

Function to modify string

push_back

push_ The back function is used to insert a character at the end of the string, but when inserting, we need to judge whether the capacity needs to be increased. If the capacity needs to be increased, call the reserve function to expand the capacity, and then put the character ch at the position of the size subscript. Note that we need to manually set the character at the position of the size+1 subscript to '\ 0', otherwise the memory will be illegally accessed when printing the string, Because the trailing character is not necessarily followed by '\ 0'.

        void push_back(char ch)
		{
			//Judge whether to increase capacity
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			_str[_size + 1] = '\0';
			++_size;
		}

We push_ The back function can also be implemented by reusing the insert function

//Trailing character
void push_back(char ch)
{
    insert(_size,ch);
}
append

The append function is used to insert a string after the string. Similarly, we also need to judge whether the size before the tail insertion and the length of the current string have exceeded the capacity. If it exceeds the capacity, we need to call the reserve function to increase the capacity. When the space is enough, we can call strcpy function to copy the string to be inserted to the end of the current string. Here is a small detail:_ The subscript position of str+size is exactly the position of the current string '\ 0', because the string to be inserted is followed by '\ 0', so we don't need to set '\ 0' after copying.

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

Similarly, the append function here can also be implemented by reusing the insert function

//Trailing string
void push_back(const char* str)
{
    insert(_size,str);
}
operator+=

The overloading of the + = operator is to realize that the + = operator can be directly used for tail interpolation between strings and characters and between strings.

+=Operator to realize the tail insertion between strings and characters. We can reuse push_back function

        //s1+='x'
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;//Support continuous + = characters
		}

+=Operator to realize the tail insertion between strings. We can reuse the append function to realize it

        //s1+="xxxxx"
		string& operator+=(const char* str)
		{
			append(str);
			return *this;//Support continuous + = string
		}
swap

The swap function is used to exchange data between two objects. We can directly call the swap template function in the library by implementing the swap function here. If we call the swap template function in the library, we need to add "::" in front of the swap (scope qualifier) and tell the compiler to find the swap function in the global scope first, If you do not add the scope qualifier, the compiler will think that you are calling the swap function you are implementing (proximity principle)

        //s1.swap(s2)
		void swap(string& s)
		{
			//Call the swap library function in the algorithm
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}
insert

We also need to implement two versions of the insert function. One version is used to insert characters and the other version is used to insert strings

When implementing the insert function to insert characters, we need to first judge whether the pos position is legal. If it is not legal, an error will be reported, and then judge whether the capacity needs to be increased. If the space is not enough, call the reserve function to increase the capacity. The process of inserting characters is realized by means of pointers. Move the characters in pos position and the characters behind pos backward by one position, empty the pos position, and then put the characters to be inserted in pos position.

        //Insert character
		string& insert(size_t pos, char ch)
		{
			//pos location must be legal
			assert(pos >= 0 && pos <= _size);
			//Judge whether to increase capacity
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//end points to the location of '\ 0'
			char* end = _str + _size;
			while (end >= _str + pos)
			{
				*(end + 1) = *end;
				--end;
			}
			//After the pos position is empty by moving the data, put ch in the pos position
			_str[pos] = ch;
			_size++;

			return *this;
		}

To implement the insert function to insert a string, we also need to judge whether the pos position is legal. If it is not legal, an error will be reported, and then judge whether the length of the string to be inserted + the effective length of the current string is greater than the space of the current string. If it is greater than the space of the current string, we call the reserve function to expand the capacity. Similar to the above, the process of inserting a string is realized by means of a pointer. Move the character at pos and the character after pos back len positions, leave enough space for the character to be inserted, and then insert the character.

It should also be noted here that we cannot use strcpy function to copy, because strcpy will copy '\ 0' in the string to be inserted, which will cause problems. Therefore, we also need to use strncpy function here.

        //Insert string
		string& insert(size_t pos, const char* str)
		{
			//pos location must be legal
			assert(pos >= 0 && pos <= _size);
			size_t len = strlen(str);
			//If the number of currently valid data and the length of the string to be inserted are greater than the space capacity, the capacity will be increased
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}
			//Mobile data
			char* end = _str + _size;
			while (end >= _str + pos)
			{
				*(end + len) = *end;
				--end;
			}
			//The len length positions starting from the pos position have been moved out
			//Note that strcpy cannot be used here, because strcpy will copy '\ 0' in str string
			//So we're going to use strncpy
			strncpy(_str + pos, str, len);
			//Renew_ size
			_size += len;

			return *this;
		}
erase

The erase function deletes n characters starting at any position in a string. Before deleting characters, we also need to judge the legitimacy of pos position. There are two situations when deleting characters.

1. The length of the remaining characters is less than the length to be deleted

This situation is relatively simple. We just need to put the pos location in '\ 0' and update our size

2. The length of the remaining characters is greater than the length to be deleted

In fact, this situation is not difficult. We need to find the next position of the last character to be deleted, and then call the strcpy function to overwrite the character at this position and the character behind it to the character position to be deleted. At this time, we don't need to manually add '\ 0' after the string, so there is' \ 0 'at the end of the string itself

string& erase(size_t pos = 0, size_t len = npos)
		{
			//pos location must be legal
			assert(pos < _size);
			//Calculate from pos position to_ Length of remaining characters in size position
			size_t lenleft = _size - pos;
			//1. The length of the remaining characters is less than the length to be deleted (all the following characters are deleted)
			if (len>lenleft)
			{
				_str[pos] = '\0';
				_size -= len;
			}
			//2. The length of the remaining characters is greater than the length to be deleted
			else
			{
				//end is the next position of the last character to be deleted
				char* end = _str + pos + len;
				//Use the strcpy function to put the following characters in the pos position. By overwriting, we delete the string to be deleted
				strcpy(_str + pos, end);
			}

			return *this;
		}
clear

To implement the clear function, we only need to set the effective length of the string to 0, and then set the position of the size subscript of the string to '\ 0'

        //Empty string
		void clear()
		{
			_size = 0;
			_str[_size] = '\0';
		}
c_str

c_str function is used to obtain the C-type string of the object and directly return the member variable of the object when it is implemented_ str is enough

        //Returns a string of type C
        char* c_str()const
		{
			return _str;
		}

String access and lookup function

find

The find function here can be implemented in two versions. One version is used to find a character and the other version is used to find a string. If it is found, it will return the subscript found for the first time. If it is not found, it will return NPOs (the maximum value of integer). Our find member function here is realized by calling the library function str.

        //Find a character
		size_t find(char ch, size_t pos = 0)const
		{
            assert(pos<_size);//Detect the legitimacy of pos location
			for (size_t i = pos; i < _size; i++)
			{
				//Return subscript when found
				if (_str[i] == ch)
				{
					return i;
				}
			}
			//Return npos not found
			return npos;
		}


       //Find a string
		size_t find(const char* str, size_t pos = 0)const
		{
            //Detect the legitimacy of pos location
            assert(pos<_size);
			const char* ret = strstr(_str+pos, str);
			//Return subscript when found
			if (ret)
			{
				//The current position minus the starting position is its subscript
				return ret - _str;
			}
			//Return to npos if not found
			else
			{
				return npos;
			}
		}
operator[]

The overloading of [] operator is implemented so that the string class object can access the characters at the corresponding position in the string through [] + subscript, just like the C string. In the C string, by [] + subscript, we can not only traverse and access a character of the current string, but also modify the content of the string. Therefore, we can also implement two versions of operator [] here, a read-only version and a readable and writable version.

        //read-only
		const char& operator[](size_t pos)const
		{
            //Detect the legitimacy of pos location
			assert(pos < _size);
			return _str[pos];
		}
        //Readable and writable
		char& operator[](size_t pos)
		{
            //Detect the legitimacy of pos location
			assert(pos < _size);
			return _str[pos];
		}

Relational operator overloaded function

Relational operators include >, > =, <, < == However, for the overloading of relational operators of any class in C + +, we only need to overload two, and the rest can be realized by reusing the overloaded operators of the first two. Here we overload < and by calling the strcmp library function.

< operator overload
 bool operator<(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}
==Operator overloading
 bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}
< = operator overload
 bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}
>Operator overloading
bool operator>(const string& s1, const string& s2)
	{
		return !(s1 <= s2);
	}
>=Operator overloading
bool operator>=(const string& s1, const string& s2)
	{
		return !(s1 < s2);
	}
!= Operator overloading
bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

Overloading of < < and > > operator and getline function

Here, the > > and < < operators are overloaded into global functions, because if overloaded into member functions, the left operand is the object of the calling function pointed to by this by default. However, when we usually knock the code, cout and cin are generally the left operands, so in order to be consistent with the usual, we overload the < < and > > operator into a global function.

< < operator overloading

Here, we can use the range for to traverse the string and output, and finally return out to support continuous output

    //output
	ostream& operator<<(ostream& out, const string& s)
	{
		//Use the range for to traverse the string and output
		for (auto e : s)
		{
			out << e;
		}
		//Support continuous output
		return out;
	}
>>Operator overloading

When we implement the > > operator overload, we need to clear the string first. If we don't clear the character first, if we have data at the beginning of the string, you don't clear the string at this time, and then input it directly, then the input string will be inserted after the original string, This will not achieve the effect we entered, so we need to clear the string first.

Note: we can't use cin or scanf to read characters here, because when using cin or scanf to read, it will ignore it when you enter a space or newline character after reading, so it can't be read. So we need to use in Get() to read characters. It won't ignore spaces or line breaks. Here's in The function of get () is similar to that of getchar in C language. When the character we read is' or '\ n', we stop reading.

//input
	istream& operator>>(istream& in, string& s)
	{
		//Clear the string first
		s.clear();
		char ch;
		//Read one character
		ch = in.get();
		//Continue reading when the read character is not a space or a newline character
		while (ch != ' '&&ch != '\n')
		{
			//The end of the read character is inserted after the string
			s += ch;
			ch = in.get();
		}
		//Support continuous input
		return in;
	}
getline

The function of getline is to read a line of string containing spaces. Its implementation is similar to that of > > operator, but it stops reading when the read character is' \ n '.

//Reads a line of string containing spaces
	istream& getline(istream& in, string& s)
	{
		//Clear the string first
		s.clear();
		char ch;
		//Read one character
		ch = in.get();
		//Continue reading when the read character is not a newline character
		while (ch != '\n')
		{
			//The end of the read character is inserted after the string
			s += ch;
			ch = in.get();
		}
		//Continuous reading
		return in;
	}

Implementation code of string class

namespace mlf
{
	// Manage the array of strings, which can be added, deleted, checked and modified
	//	//String array ends with \ 0
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
		
		//Readable and writable
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}

		//read-only
		const_iterator begin()const
		{
			return _str;
		}

		const_iterator end()const
		{
			return _str + _size;
		}
		//Constructor
		string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			//Make room for storing strings. Give '\ 0' the extra one
			_str = new char[_capacity + 1];
			//Copy the C string to the opened space
			strcpy(_str, str);
		}
		//Destructor
		~string()
		{
			delete[]_str;
			_str = nullptr;
			_size = 0;
			_capacity = 0;
		}

		//s1.swap(s2)
		void swap(string& s)
		{
			//Call the swap library function in the algorithm
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}

		//copy construction 
		//s1(s3)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string temp(s._str);
			/*swap(_str, temp._str);
			swap(_size, temp._size);
			swap(_capaticy, temp._capaticy);*/
			swap(temp);
		}

		//Assignment operator overload
		//s1 = s3
		string& operator=(string s)
		{
			/*swap(_str, s._str);
			swap(_size, s._size);
			swap(_capaticy, s._capaticy);*/
			swap(s);

			return *this;
		}

		//read-only
		const char& operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}

		//Readable and writable
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		// Open space + initialize, expand capacity and initialize space. I want to move, too
		void resize(size_t n,char ch = '\0')
		{
			//If n is less than_ size
			if (n < _size)
			{
				//Then the effective number is n
				_size = n;
				_str[_size] = '\0';
			}
			//n greater than_ size
			else
			{
				//If n is greater than capacity
				//We'll add capacity
				if (n>_capacity)
				{
					reserve(n);
				}
					begin point'\0'Location of
					//char* start = _str + _size;
					//char* end = _str + n;
					//while (start != end)
					//{
					//	*start++ = ch;
					//	++_size;
					//}
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
					_str[n] = '\0';
					_size = n;
			}
		}

		// Open space and expand capacity
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				//Open n+1 spaces and one for '\ 0'
				char* temp = new char[n + 1];
				//Copy the original data to temp
				//Copy the last '\ 0' too
				strncpy(temp, _str,_size+1);
				//Release_ str
				delete[]_str;
				//Let again_ str points to temp
				_str = temp;
				//Then update capacity
				_capacity = n;
			}
		}

		void push_back(char ch)
		{
			//Judge whether to increase capacity
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			_str[_size + 1] = '\0';
			++_size;
		}

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

		//s1+='x'
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		//s1+="xxxxx"
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		//Insert character
		string& insert(size_t pos, char ch)
		{
			//pos location must be legal
			assert(pos >= 0 && pos <= _size);
			//Judge whether to increase capacity
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//end points to the location of '\ 0'
			char* end = _str + _size;
			while (end >= _str + pos)
			{
				*(end + 1) = *end;
				--end;
			}
			//After the pos position is empty by moving the data, put ch in the pos position
			_str[pos] = ch;
			_size++;

			return *this;
		}

		//Insert string
		string& insert(size_t pos, const char* str)
		{
			//pos location must be legal
			assert(pos >= 0 && pos <= _size);
			size_t len = strlen(str);
			//If the number of currently valid data and the length of the string to be inserted are greater than the space capacity, the capacity will be increased
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}
			//Mobile data
			char* end = _str + _size;
			while (end >= _str + pos)
			{
				*(end + len) = *end;
				--end;
			}
			//The len length positions starting from the pos position have been moved out
			//Note that strcpy cannot be used here, because strcpy will copy '\ 0' in str string
			//So we're going to use strncpy
			strncpy(_str + pos, str, len);
			//Renew_ size
			_size += len;

			return *this;
		}

		string& erase(size_t pos = 0, size_t len = npos)
		{
			//pos location must be legal
			assert(pos < _size);
			//Calculate from pos position to_ Length of remaining characters in size position
			size_t lenleft = _size - pos;
			//1. The length of the remaining characters is less than the length to be deleted (all the following characters are deleted)
			if (len>lenleft)
			{
				_str[pos] = '\0';
				_size -= len;
			}
			//2. The length of the remaining characters is greater than the length to be deleted
			else
			{
				//end is the next position of the last character to be deleted
				char* end = _str + pos + len;
				//Use the strcpy function to put the following characters in the pos position. By overwriting, we delete the string to be deleted
				strcpy(_str + pos, end);
			}

			return *this;
		}

		//Find a character
		size_t find(char ch, size_t pos = 0)const
		{
            assert(pos<_size);//Detect the legitimacy of pos location
			for (size_t i = pos; i < _size; i++)
			{
				//Return subscript when found
				if (_str[i] == ch)
				{
					return i;
				}
			}
			//Return npos not found
			return npos;
		}

		//Find a string
		size_t find(const char* str, size_t pos = 0)const
		{
            assert(pos<_size);//Detect the legitimacy of pos location
			const char* ret = strstr(_str+pos, str);
			//Return subscript when found
			if (ret)
			{
				//The current position minus the starting position is its subscript
				return ret - _str;
			}
			//Return to npos if not found
			else
			{
				return npos;
			}
		}

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

		//Air judgment
		bool empty()const
		{
			return strcmp(_str, "") == 0;
		}

		char* c_str()const
		{
			return _str;
		}

		size_t size()const
		{
			return _size;//Returns the valid length of the current string
		}
        
         size_t capacity()const
		{
			return _capacity;//Returns the capacity of the current string
		}
		private:
			char* _str;
			size_t _size;
			size_t _capacity;

			static const size_t npos;
	};

	 bool operator<(const string& s1, const string& s2)const
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}

	 bool operator==(const string& s1, const string& s2)const
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}
    //After the above < and = = are implemented, we can reuse < and = =
	 bool operator<=(const string& s1, const string& s2)const
	{
		return s1 < s2 || s1 == s2;
	}

	 bool operator>(const string& s1, const string& s2)const
	{
		return !(s1 <= s2);
	}

	bool operator>=(const string& s1, const string& s2)const
	{
		return !(s1 < s2);
	}

	bool operator!=(const string& s1, const string& s2)const
	{
		return !(s1 == s2);
	}

	//output
	ostream& operator<<(ostream& out, const string& s)
	{
		//Use the range for to traverse the string and output
		for (auto e : s)
		{
			out << e;
		}
		//Continuous output
		return out;
	}
	//input
	istream& operator>>(istream& in, string& s)
	{
		//Clear the string first
		s.clear();
		char ch;
		//Read one character
		ch = in.get();
		//Continue reading when the read character is not a space or a newline character
		while (ch != ' '&&ch != '\n')
		{
			//The end of the read character is inserted after the string
			s += ch;
			ch = in.get();
		}
		//Continuous input
		return in;
	}
	//Reads a line of string containing spaces
	istream& getline(istream& in, string& s)
	{
		//Clear the string first
		s.clear();
		char ch;
		//Read one character
		ch = in.get();
		//Continue reading when the read character is a newline character
		while (ch != '\n')
		{
			//The end of the read character is inserted after the string
			s += ch;
			ch = in.get();
		}
		//Continuous reading
		return in;
	}

	const size_t string::npos = -1;

The above is the whole content of simulating the implementation of string class. If you think the article is helpful to you, you can give the author three consecutive waves. Thank you for your support.

Keywords: C++ Back-end

Added by micbox on Wed, 02 Feb 2022 18:29:41 +0200