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.