[C++] Simulate the implementation of the string class

Directory

foreword

member variable of string

How to create string object

Constructor

copy construction

string output

change capacity

insert

insert string

+ = operation

insert insert

delete

look up

stream insertion (<<) and stream extraction (>>) overloads


Preface

The string class is an important component in the C++ standard library, which provides a convenient way to manipulate strings. Compared with the character array in C language, the string class is more flexible and easy to use. It can easily handle strings of dynamic size, and also provides many important string operation methods, such as splicing, splitting, replacing, searching, etc.

The string class is essentially an array that encapsulates a string, and the size of the string can be adjusted as needed at runtime. An important advantage of its design is automatic memory management, that is, it is responsible for adjusting the size of memory, avoiding the problem of memory overflow or space waste. Next, let’s simulate the common interface that implements string.

If there are mistakes, please correct me.

member variable of string

private:
char* _str;//Point to the string
size_t _size;//valid data
size_t _capacity;//The size of the character array space

1._size indicates the number of valid characters excluding the space of ‘ \0 ‘.

2._capacity means that the effective space size also does not include the space of ‘\0’.

3. When initially constructing a string object, _size and _capacity have the same size.

4. When applying for space to store strings in the memory, the size of the opened space is 1 larger than _capacity for storing ‘ \0 ‘.

How to create a string object

 string s1;//No parameter construction
string s2 = "hello world";//construct with parameters
string s3(s2);//copy construction
string s4 = s3;//Assignment overload construction

No-argument construction can give a default value in the argument constructor, the default value is ” “, the string comes with ‘\0’

Constructor

string(const char* str = " ") :_size(strlen(str))
{
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}

copy construction

We have applied for memory in the heap area. If the copy constructor is not explicitly defined, the copy constructor generated by the compiler by default completes a shallow copy. When we call the copy constructor generated by the compiler to construct an object, the object’s The string pointer also points to the space pointed to by the string pointer of the parameter object. When the life cycle of the object ends, the compiler will automatically call the destructor, thereby re-destructing the same space.

Shallow copy

//shallow copy
string(const string & s)
{
_size = s._size;
_capacity = s._capacity;
_str = s._str;
}

Deep copy

Re-apply for a space in the heap area, copy the content in, and let the character pointer variable pointer point to this space;

string(const string & s):_size(s._size),_capacity(s._capacity)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
}

Assignment Overload

string & amp; operator=(const string & amp; str)
{
if ( &str != this)
{
char* tmp = new char[_capacity + 1];
strcpy(tmp, str._str);
delete[]_str;
_str = tmp;
_size = str._size;
_capacity = str. _capacity;
}
return *this;
}

Destructor

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

string output

Call this function to get the pointer of the object to the string and print it;

const char* c_str()
{
return_str;
}

[ ] operator overloading

In some cases, we need to output a string from a certain position, such as printing world for the string “hello world”, then we need the [ ] operator. For custom types, we need to operate on [ ] ourselves character to overload.

 // can only read and cannot change the string
const char & operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
// can be read or changed
char & operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}

It can be seen from the output that the ASCII code of the string is increased by 1.

iterator

Behavior is like a pointer. Iterators in the VS environment are not implemented with pointers, but we can encapsulate a pointer to simulate.

typedef char* iterator;
//begin() points to the first element of the string
iterator begin()
{
return_str;
}



//end() points to \0
iterator end()
{

return _str + _size;

Next we can use iterators to traverse the string. Range for simply replaces iterators.

 string s1 = "1234567890";
clastr::string::iterator it1;

for (it1 = s1.begin(); it1 != s1.end(); + + it1)
{
cout << (*it1);
}
cout << endl;

        // range for
        for (auto ch: s1)
{
cout << ch<<" ";
}
cout << endl;

Change capacity

When we add or delete a string, the capacity of the string changes, so we need to expand the capacity. Apply for a certain amount of memory to the heap area, copy the contents of the original memory space, and release the original space. The following are the implementation steps of capacity expansion.

 void reserve(size_t n)
{
if (n > _capacity)//no shrinkage
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n;
}
}

The above is the change in the size of the memory space. Next, let’s analyze the change in the size of the effective string. There are three cases of change.

When the valid string subscript n is less than or equal to the original _size, you only need to change the value of _size to n, and put a ‘\0’ at the position where the subscript is n.
The effective string subscript n is larger than the original _size and smaller than the original _capacity. At this time, there is no need to expand the capacity, just set the original _size to n, and use Formal parameter ch to fill, if no ch is passed, the default value ‘\0’ will be used.
The effective string subscript n is greater than the original _capacity, and expansion is required at this time, and subsequent operations are the same as above.

 void resize(size_t n, char ch = '\0')
{
if (n <= _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
if (n > _capacity)
{
reserve(n);
}
size_t i = _size;
while (i<n)
{
_str[i++] = ch;
}
_size = n;
_str[_size] = '\0';
}
}

insert

Insert character: Insert a character at the end of the original string.

 string & amp; push_back(char ch)
{
if (_size + 1 > _capacity)
{
reserve(_capacity*2);
}
_str[_size] = ch;
+ + _size;
_str[_size] = '\0';
return *this;

}

insert string

 string & amp; append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
return *this;

}

+ =action

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

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

insert insert

Inserts a character at the specified position.

Inserts a string at the specified position.

string & amp; insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size + len > _capacity)//check capacity
{
reserve(_size + len);
}
size_t end = _size + len;
while (end > pos + len - 1)
{
_str[end] = _str[end - len];//move data
--end;
}
strncpy(_str + pos, str, len);
_size = _size + len;
return *this;
/*size_t len = strlen(str);
if (_size + len>_capacity)
{
reserve(_size + len);
}
int end = _size + len - 1;
int gap = _size - pos + 1;

while (gap--)
{
_str[end] = _str[end - len];
--end;

}
strncpy(_str + pos, str, len);
_str[_size + len] = '\0';
_size = _size + len;*/
\t\t\t
}

Delete

Here we define npos, which is a static member variable. When it is declared in the class, give it a default value of -1, and this default value will be used when it is defined.
Static member variables are declared inside the class, and must be defined outside the class, but there is a special type, static members of the integer family can be given a default value at the time of declaration.
Since it is -1 of the size_t type, its actual size is 32 binary 1s, which is roughly 4.29 billion when converted into decimal.

const static size_t npos=-1;
 string & amp; erase(size_t pos, size_t len = npos)
{
assert(pos < _size);

if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}

Find

 size_t find(char ch, size_t pos=0)
{
assert(pos < _size);

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);
char* p = strstr(_str + pos,str);
if (p == nullptr)
{
return npos;
}
else
{
return p - _str;
}
}

Tagged , , , , , ,