Simulate the string class–[C++]

W…Y’s homepage

Code repository sharing

Foreword:

We have already understood and mastered all the important interfaces of the string class in STL. In order to give us a deeper understanding of string and C++ classes and objects, our blog will simulate and implement the string class.

Table of Contents

Simulation implementation of string class

Constructor and destructor

copy constructor

The rest of the string class object interface simulation implementation


Simulation implementation of string class

Our first step is to distinguish the difference between the strings we simulate and implement and the strings in STL, so we have to use named fields to distinguish them. Then there is the setting of private members. The bottom layer of string is an array, so we have to create a character pointer. There are two variables: _size to detect the size of the content in the array, and _capacity to detect the space size of the array. The data in the sequence table is the same.

Constructor and Destructor

These two functions are very critical and carry the entire string class. The initialization and destruction of string rely on these two functions.

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

Because we know that in the constructor provided by STL, we can pass parameters or not for initialization, so we also use this initialization type. When we do not pass parameters, an empty string is created by default. When we pass the parameters, the constructor we created will allocate the parameter of the size of the passed string by default, otherwise it will open a space of 4 bits. If we choose not to open up space without passing parameters, conflicts will occur in subsequent functions.

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

The destructor is very simple. It releases the opened space and finally controls _size and _capacity.

copy constructor

For the copy constructor, we can no longer use the copy constructor generated by C++ by default, because the data type is no longer a simple built-in type, and there is space created by us. If we use shallow copy, two data types will be released in the destructor. The same space causes the program to crash.

If a class involves resource management, its copy constructor, assignment operator overloading, and destructor must be explicitly given. Generally, it is provided in deep copy mode.

Use deep copy so that each object has an independent resource and do not share it with other objects.

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

Other string class object interface simulation implementation

In the simulation implementation of string, we have many similar interfaces that can be implemented. Here I want to say that when it comes to simulation implementation, we can simply implement it by reusing various functions in the C library without the need for large-scale writing. code, which consolidates our use of functions.

The following is the complete code for the simulation implementation of the string class function:

string.h

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace why
{
class string
{
public:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
typedef const char* const_iterator;
iterator begin()const
{
return _str;
}
iterator end()const
{
return _str + _size;
}
/*string()
:_str(new char[1])
,_size(0)
,_capacity(0)
{
_str[0] = '\0';
}*/
string(const char* str = "")
:_size(strlen(str))
{
_capacity = _size == 0 ? 4 : _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
const char* c_str()
{
return _str;
}
char & operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char & amp; operator[](size_t pos)const
{
assert(pos < _size);
return _str[pos];
}
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//copy construction
string(const string & s)
:_size(s._size)
, _capacity(s._capacity)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
}
string & amp; operator=(const string & amp; s)
{
if (this != & amp;s)
{
/*delete[] _str;
_str = new char[s._capacity + 1];
strcpy(_str, s._str);*/
//Prevent the failure of new to open up space and cause the original data to be released
char* tmp = new char[s._capacity + 1];
strcpy(tmp, s._str);
_str = tmp;
_size = s._size;
_capacity = s._capacity;

}
return *this;
}
bool operator==(const string & s)const
{
return strcmp(_str, s._str) == 0;
}
bool operator>(const string & s)const
{
return strcmp(_str, s._str) > 0;
}
bool operator<(const string & s)const
{
return !(*this > s || *this == s);
}
bool operator<=(const string & s)const
{
return *this == s || *this < s;
}
bool operator>=(const string & s)const
{
return *this == s || *this > s;
}
bool operator!=(const string & amp; s)const
{
return !(*this == s);
}
void reserve(size_t n)
{
if (_capacity < n)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size + 1 > _capacity)
{
reserve(_capacity * 2);
}
_str[_size] = ch;
_size + + ;
_str[_size] = '\0';
}
void append(const char* s)
{
size_t len = sizeof(s);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, s);
_size + = len;
}
string & operator + =(char ch)
{
push_back(ch);
return *this;
}
string & operator + =(const char* s)
{
append(s);
return *this;
}
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';
}
}
void insert(size_t pos, char x)
{
assert(pos <= _size);
if (_size + 1 > _capacity)
{
reserve(2 * _capacity);
}
size_t _end = _size + 1;
while (_end > pos)
{
_str[_end] = _str[_end - 1];
_end--;
}
_str[pos] = x;
_size + + ;
}
string & insert(size_t pos, const char* s)
{
assert(pos <= _size);
size_t len = strlen(s);
if (_size + len > _capacity)
{
reserve(_size + len);
}
size_t end = _size + len;
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
--end;
}
/*size_t i = 0;
while (i < len)
{
_str[pos] = s[i];
pos + + ;
i + + ;
}*/
strncpy(_str + pos, s, len);
_size + = len;
return *this;
}
string & 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;
}
void swap(string & s)
{
std::swap(_str, s._str);
std::swap(_capacity, s._capacity);
std::swap(_size, s._size);
}
size_t find(char ch, size_t pos = 0)
{
assert(pos < _size);
for (size_t i = pos; i < _size; + + i)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t find(const char* str, size_t pos = 0)
{
assert(pos < _size);

//kmp
char* p = strstr(_str + pos, str);
if (p == nullptr)
{
return npos;
}
else
{
return p-_str;
}
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
private:
char* _str;
size_t _size;
size_t _capacity;
static const size_t npos;
//static const size_t npos = -1;
};
const size_t string::npos = -1;
void print(const string & s)
{
for (int i = 0; i < s.size(); i + + )
{
cout << s[i] << ' ';
}
cout << endl;
string::const_iterator it = s.begin();
while (it != s.end())
{
cout << *it << ' ';
+ + it;
}
cout << endl;
for (auto ch : s)
{
cout << ch << ' ';
}
cout << endl;
}
\t
void test1()
{
string s1;
string s2("hello world");
std::cout << s1.c_str() << std::endl;
std::cout << s2.c_str() << std::endl;
s2[0] + + ;
std::cout << s2.c_str() << std::endl;
}
void test2()
{
string s1;
string s2("hello world");
string s3(s2);
std::cout << s2.c_str() << std::endl;
std::cout << s3.c_str() << std::endl;
s2[0] + + ;
std::cout << s2.c_str() << std::endl;
std::cout << s3.c_str() << std::endl;
s1 = s3;
std::cout << s1.c_str() << std::endl;
}
void test3()
{
string s1("hello world");
print(s1);
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << ' ';
+ + it;
}
cout << endl;
for (auto ch : s1)
{
cout << ch << ' ';
}
cout << endl;
}
void test4()
{
string s1("hello world");
string s2("hello world");
string s3("wwwwwww");
cout << (s1 == s2) << endl;
cout << (s1 != s2) << endl;
cout << (s1 > s2) << endl;
}
void test5()
{
string s1 = "hello world";
cout << s1.c_str() << endl;
string s2 = "xxxxxxxxxx";
s1.append("xxxxxxxx");
cout << s1.c_str() << endl;
s1 + = 'c';
s2 + = "yyyyyyy";
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
string s3;
s3 + = "ssssssss";
cout << s3.c_str() << endl;
string s4;
s4 + = '1';
cout << s4.c_str() << endl;
}
void test6()
{
string s1("hello worlddddddddddddddd");
cout << s1.capacity() << endl;
s1.reserve(10);
cout << s1.capacity() << endl;
}
void test7()
{
string s1;
s1.resize(20, 'x');
cout << s1.c_str() << endl;
s1.resize(30, 'y');
cout << s1.c_str() << endl;
s1.resize(10);
cout << s1.c_str() << endl;
}
void test8()
{
string s1("xxxxx");
s1.insert(0, 'y');
cout << s1.c_str() << endl;
s1.insert(0, "wwwwww");
cout << s1.c_str() << endl;
}
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"

int main()
{
//why::test1();
//why::test2();
//why::test3();
//why::test4();
//why::test5();
//why::test7();
why::test8();
return 0;
}

When we simulate the implementation, we must test while writing to correct the problems in modules, so that a large amount of code can be completed as quickly as possible.

The above is the entire content of this blog, thank you all for watching.