C++ simulation implementation string

Article directory

  • foreword
  • 1. Related header files included
  • 2. Construction and destruction
    • 1. Constructor
    • 2. Copy construction
      • 1. Traditional writing
      • 2. Modern writing
    • 3. Assignment operator overloading
      • 1. Traditional writing
      • 2. Modern writing
    • 4. Destructor
  • Three, iterator
  • Four, modify
    • 1.push_back (insert a character at the end)
    • 2.append (insert a string at the end)
    • 3. Operator overloading +=
      • 1. Tail caret
      • 2. Tail insertion string
    • 4.clear
    • 5. insert
      • 1. Insert a character
      • 2. Insert a string
    • 6. erase
  • Five, capacity
    • 1. size
    • 2.capacity
    • 3.empty
    • 4. resize
    • 5. reserve
  • Six, access
    • 1. Common object interface (readable and writable)
    • 2. The interface of the const object (read-only)
  • Seven, relational operators
    • 1. Operator
    • 2. Operator > Overloading
    • 3. Operator <= overloading
    • 4. Operator >= overloading
    • 5. Operator == overloading
    • 6. Operator != Overloading
  • 8. String operations
    • 1. find
      • 1. Find characters
      • 2. Find substring
    • 2. c_str
  • 9. Non-member function overloads
    • 1. Stream insertion
    • 2. Stream extraction
  • 10. Private property
  • Summarize

Foreword

Because I have learned the relevant knowledge of string and the underlying implementation principles of most of the interfaces of string, I decided to simulate and implement a mini version of the string class by myself to deepen my understanding of all aspects of string.
If there are any mistakes or deficiencies, I hope that readers and friends will point them out.

1. Related header files included

#include<iostream>
#include <assert.h>
#include <cstring>
using std::ostream;
using std::istream;
using std::cout;
using std::endl;

2. Construction and destruction

1. Constructor

 //Construction
string(const char* str = "")
{<!-- -->
size_t len = strlen(str);
_capacity = _size = len;
_str = new char[_capacity + 1];
strcpy(_str, str);
}

2. Copy construction

1. Traditional writing

The object makes a deep copy bit by bit

 //copy construction
string(const string & s)
{<!-- -->
_str = new char[s._capacity + 1];
_size = s._size;
_capacity = s._capacity;
strcpy(_str, s._str);
}

2. Modern writing

Find an intermediate object, let this intermediate object be directly constructed with the value of the parameter, and then exchange the content of this intermediate object with its own content. Compared with traditional writing, modern writing is more concise.

 //copy construction
string(const string & s)
:_str(nullptr),//Attention should be paid to assigning the address of the object as nullptr here, otherwise the program will crash due to wild pointer access when the intermediate object is destructed.
_size(0),
_capacity(0)
{<!-- -->
string temp(s);
swap(temp);
}

The swap here uses the swap implemented by string itself, why not use the swap in the library? Because the swap in the library is a deep copy, it will reduce efficiency. The swap we implemented ourselves only needs to perform a shallow copy, that is, to exchange their corresponding resources.

 void swap(string & s)
{<!-- -->
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}

3. Assignment operator overloading

1. Traditional writing

 //Assignment operator overloading
string & amp; operator=(const string & amp;s)
{<!-- -->
if (this != &s)
{<!-- -->
char* temp = new char[s._capacity + 1];//Use an intermediate value to record the newly opened space, if the space is opened successfully, then give the address of the space to _str. Avoid the situation where the original content of _str is also destroyed due to the failure to open up space.
strcpy(temp, s._str);
delete[]_str;
_str = temp;
_capacity = s._capacity;
_size = s._size;
}
return *this;
}

2. Modern writing

Similar to the modern writing idea of copy construction, it can make the program more concise. And there is no need to specifically define an intermediate object here, because the formal parameter passed by value and parameter is an object constructed by copying the actual parameter. It is a good intermediate object, and we can directly exchange it with it.

 //Assignment operator overloading
string & operator=(string s)
{<!-- -->
swap(s);
return *this;
}

4. Destructor

 //destruction
~string()
{<!-- -->
delete[]_str;
_str = nullptr;//After releasing the space pointed to by the pointer, set the pointer to null (to avoid illegal access of wild pointers)
_size = _capacity = 0;
}

3. iterator

An iterator is something that looks like a pointer. In fact, an iterator of a string is a pointer of type char*, so we typedef at the very beginning

typedef char* iterator;

Define two iterators: point to the beginning and end of the string respectively

 iterator begin()
{<!-- -->
return_str;
}
iterator end()
{<!-- -->
return _str + _size;
}

4. modify

1.push_back (insert a character at the end)

String only provides the interface of push_back but not the interface of plug-in, because plug-in needs to move data, which is very inefficient, so try not to use plug-in in string.

 void push_back(char c)
{<!-- -->
if (_size == _capacity)
{<!-- -->
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;//Take into account if the capacity of the initial string is 0
reserve(newcapacity);
_capacity = newcapacity;
}
_str[_size++] = c;
_str[_size] = '\0';
}

2.append (insert a string at the end)

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

3. Operator overloading + =

1. Trailing characters

Insert a character at the end (multiplexing end insertion)

 string & amp; operator + =(char c)
{<!-- -->
push_back(c);
return *this;
}

2. Tail insert string

Insert a string at the end (reuse append)

 string & amp; operator + =(const char* str)
{<!-- -->
append(str);
return *this;
}

4.clear

Clear all elements in string

 void clear()
{<!-- -->
_size = 0;
_str[0] = '\0';
}

5.insert

1. Insert a character

Insert a character at a position in a string:

 // insert character c/string str at position pos
string & amp; insert(size_t pos, char c)
{<!-- -->
assert(pos <= _size);
if (_size + 1 > _capacity)
{<!-- -->
reserve(_size + 1);
}
for (size_t i = _size + 1; i > pos; --i)//Be careful not to reduce i to -1 and become a huge unsigned number, resulting in an infinite loop
{<!-- -->
_str[i] = _str[i - 1];
}
_str[pos] = c;
+ + _size;
return *this;
}

2. Insert a string

Insert a string at a position in a string:

 string & amp; insert(size_t pos, const char* str)
{<!-- -->
assert(pos <= _size);
size_t i = 0;
size_t len = strlen(str);
if (_size + len > _capacity)
{<!-- -->
reserve(_size + len);
}
for (i = _size + len; i > pos + len - 1; --i)
{<!-- -->
_str[i] = _str[i - len];
}
strncpy(_str + pos, str, len);
_size += len;
return *this;
}

6. erase

Delete a substring starting from a certain position in the string and a certain length (if the length of the deleted substring is not specified, all content from the beginning position to the end will be deleted by default)

 // Delete the element at position pos
string & erase(size_t pos, size_t len = npos)
{<!-- -->
if (len == npos || pos + len > _size)
{<!-- -->
_str[pos] = '\0';
_size = pos;
}
else
{<!-- -->
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}

5. capacity

1.size

Members outside the class cannot directly access the private properties/data of the class, so an interface needs to be provided to access the data, but the data must be protected, so const parameter passing and value returning are used.
Access the number of elements in string.

 size_t size() const
{<!-- -->
return_size;
}

2. capacity

Access the capacity of string.

 size_t capacity() const
{<!-- -->
return_capacity;
}

3.empty

Check if the string is empty.

 bool empty() const
{<!-- -->
if (_size == 0)
{<!-- -->
return true;
}
return false;
}

4. resize

Modify the number of elements in the string, and the specified character can be inserted at the end. If not specified, ‘\0’ will be inserted by default.

 void resize(size_t n, char c = '\0')//change the number of elements
{<!-- -->
if (n <= _size)
{<!-- -->
_size = n;
_str[_size] = '\0';
}
else
{<!-- -->
reserve(n);//Expand first, then add elements
for (size_t i = _size; i < n; + + i)
{<!-- -->
_str[i] = c;
}
_str[n] = '\0';
_size = n;
}
}

5. reserve

Modify the capacity of the string.

 void reserve(size_t n)//Change capacity size
{<!-- -->
if (n > _capacity)
{<!-- -->
char* temp = new char[n + 1];//Open the space first and then copy the value (to avoid the loss of the original address of _str due to the exception of space opening failure)
strcpy(temp, _str);
delete[]_str;
_str = temp;
_capacity = n;
}
}

6. access

To access characters in a string, operator[] is overloaded.

1. Common object interface (readable and writable)

 char & amp; operator[](size_t index)//Common object interface (readable and writable)
{<!-- -->
assert(index < _size);
return _str[index];
}

2.const object interface (read-only)

 const char & amp; operator[](size_t index)const//const object interface (read-only)
{<!-- -->
assert(index < _size);
return _str[index];
}

7. relational operators

1. Operator
 bool operator<(const string & s)
{<!-- -->
size_t len = _size < s._size ? _size : s._size;
for (size_t i = 0; i < len; + + i)
{<!-- -->
if ((*this)[i] >= s[i])
{<!-- -->
return false;
}
return true;
}
}

2. Operator>Overload

 bool operator>(const string & s)
{<!-- -->
size_t len = _size < s._size ? _size : s._size;
for (size_t i = 0; i < len; + + i)
{<!-- -->
if ((*this)[i] <= s[i])
{<!-- -->
return false;
}
return true;
}
}

3. Operator <=overloading

Multiplexing>.

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

4. Operator>=overload

multiplexing <.

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

5. Operator == overload

Multiplexing < and >.

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

6. Operator != Overloading

multiplexing ==.

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

8. String operations

1.find

1. Find characters

Find the subscript of the first occurrence of a character in a string

 // returns the first occurrence of c in the string (subscript)
size_t find(char c, size_t pos = 0) const
{<!-- -->
assert(pos < _size);
for (size_t i = 0; i < _size; + + i)
{<!-- -->
if ((*this)[i] == c)
{<!-- -->
return i;
}
}
return npos;
}

2. Find substring

Find the subscript where a substring occurs for the first time in a string
Reuse the operation of C language on strings – strstr function (find substring in a string)

 // returns the first occurrence of substring s in string (subscript)
size_t find(const char* s, size_t pos = 0) const
{<!-- -->
assert(pos < _size);
const char* ptr = strstr(_str + pos, s);
if (ptr == nullptr)
{<!-- -->
return npos;
}
else
{<!-- -->
return ptr - _str;
}
}

You can also traverse character by character to find:

 // returns the first occurrence of substring s in string (subscript)
size_t find(const char* s, size_t pos = 0) const
{<!-- -->
assert(pos < _size);
size_t i = 0;
while (i < _size)
{<!-- -->
int flag = 0;
if ((*this)[i] == s[0])
{<!-- -->
size_t k = i;
for (size_t j = 0; j < strlen(s); + + j, + + k)
{<!-- -->
if ((*this)[k] != s[j])
{<!-- -->
flag = 1;
}
}
if (flag == 0) return i;
}
i + + ;
}
return npos;
}

2.c_str

Returns the address of the string

 const char* c_str() const
{<!-- -->
return_str;
}

9. Non-member function overloads

1. Flow insertion

 ostream & amp; operator<<(ostream & amp; _cout, const string & amp; s)
{<!-- -->
for (size_t i = 0; i < s. size(); i ++ )
{<!-- -->
_cout << s[i];
}
return_cout;
}

2. Stream extraction

 istream & amp; operator>>(istream & amp; _cin, string & amp; s)
{<!-- -->
s.clear();//clear the value in the original s
char buff[128] = {<!-- --> '\0' };
size_t i = 0;
char ch = _cin.get();//The reason why the getc or scanf function cannot be used here is that they all divide the content according to the space' ' or newline'\
', so the space and newline cannot be obtained, so only use get()
while (ch != ' ' || ch != '\
')
{<!-- -->
if (i == 127)//The cache is full
{<!-- -->
s + = buff;//Save the content into s, and cache part of the situation at the same time
i = 0;
}
buff[i + + ] = ch;
ch = _cin. get();
}
return _cin;
}

10. Private attributes

 private:
char*_str;
size_t_capacity;
size_t_size;
const static size_t npos = -1;//Only this special case can be defined in this way, other static data members must be declared inside the class and defined outside the class.

Summary

The above is what I want to talk about today. This article introduces the related class member functions of the string class implemented by the author. If there are errors or imprecise parts in the article, you are welcome to point it out in the comment area, and you are welcome to ask questions in the comment area ,comminicate.
Finally, if this article inspires you, I hope you can support the author a lot, thank you!