1, Define my_string class
#include<iostream> #include<assert.h> #include<string> using namespace std; class my_string{ private: char* _ptr; //character string size_t _size; //Number of valid characters size_t _capacity; //capacity static size_t npos; //size_ Maximum number in type T (indicates - 1) public: //exchange void Swap(my_string& str) { swap(_ptr, str._ptr); swap(_size, str._size); swap(_capacity, str._capacity); } //Member functions and other functions are implemented below }; size_t my_string::npos = -1;
2, Member function
(1) Constructor
if additional resources are needed in the object, the write constructor is displayed;
the constructor parameter defaults to an empty string and is written as a default construct;
my_string(char* str = "") { _size = strlen(str); _capacity = _size; _ptr = new char[_size + 1]; //Open one_ size+1 space, leave a place for '\ 0' strcpy(_ptr, str); }
one is required in the constructor_ The space of size+1 is used to copy the string to the object, leaving a position of \ 0;
(2) Destructor
there is additional resource overhead in the object, so it is necessary to display and write out the destructor;
~my_string() { //If the object resource is not nullptr, destroy it if (_ptr) { delete[] _ptr; _ptr = nullptr; _size = 0; _capacity = 0; } }
(3) Copy construction
1. Traditional writing method
my_string(const my_string& str) :_ptr(new char[str._capacity + 1]) //Str_ The space of capacity + 1 is because_ Capacity is the maximum effective capacity, excluding '\ 0' , _size(str._size) , _capacity(str._capacity) { strcpy(_ptr, str._ptr); }
2. Simple writing method
a simple way to write is to initialize the object itself as empty, and then create a temporary object through the constructor to exchange with the object itself to achieve the effect of copying;
my_string(const my_string& str) :_ptr(nullptr) //Let here_ ptr = nullptr is to prevent the destruction error when destroying temporary objects , _size(0) , _capacity(0) { //Create a temporary object my_string tmp(str._ptr); //Exchange with temporary objects Swap(tmp); }
_ The purpose of ptr(nullptr) is that the temporary object can run normally when calling destructor. If it is not initialized to nullptr, then_ ptr will point to the address in memory, so there will be problems during re destruct and destruction;
(4) Assignment operator overload
if the object has resource overhead, it shows that the assignment operator is written out to overload the function to complete the deep copy of the resource;
1. Traditional writing method
my_string& operator=(const my_string& str) { //If you don't assign a value to yourself, enter judgment if (this != &str) { //Open space char* tmp = new char[str._capacity + 1]; //Release the original space delete[] _ptr; //Copy strcpy(tmp, str._ptr); //Change the pointer to complete the deep copy _ptr = tmp; _size = str._size; _capacity = str._capacity; } return *this; }
2. Simple writing method
my_string& operator=(my_string str) { Swap(str); return *this; }
the deep copy of this writing method has been carried out when the parameter is transmitted. When the parameter is received, a copy structure is used to copy the object to be assigned, and then the resource exchange is carried out with the assigned object to complete the deep copy assignment;
3, Iterator
the bottom layer of the iterator is actually a pointer, but the pointer is nicknamed iterator, so it is implemented with a pointer;
begin is actually to get the address of the first element; Get the next address of the last element;
it should be noted that const iterators must use const to modify functions;
//iterator typedef char* iterator; typedef const char* const_iterator; iterator begin() { return _ptr; } iterator end() { return _ptr + _size; //Points to the next location of the last index } const_iterator begin() const { return _ptr; } const_iterator end() const { return _ptr + _size; } const_iterator cbegin() const { return _ptr; } const_iterator cend() const { return _ptr + _size; }
4, Capacity
(1) Get string capacity
1. Get the number of valid elements: size()
size_t size() { return _size; }
2. Get the maximum effective element capacity: capacity()
size_t capacity() { return _capacity; }
3. Air judgment
bool empty() const { if (_size == 0) return true; return false; }
(2) Change capacity
1. Capacity expansion: reserve()
when the capacity is insufficient, it needs to be expanded and a larger space needs to be reopened;
void reserve(size_t n) { if (n >= _capacity) { //Open space char* tmp = new char[_capacity + 1]; //Copy strcpy(tmp, _ptr); //Delete existing space delete[] _ptr; //Change direction _ptr = tmp; _capacity = n; } }
2. Change the number of effective elements: resize()
changing capacity resize usually includes the following aspects:
(1)n < _size: Volume reduction (2)_size < n < _capacity: increase capacity + assignment (3)n > _capacity: Capacity expansion + increase capacity + assignment
void resize(size_t n, char ch = '\0') { if (n > _size) { if (n > _capacity) { reserve(n); } memset(_ptr + _size, ch, (n - _size) * sizeof(char)); } _size = n; _ptr[_size] = '\0'; }
5, Element access
(1) [] operator overload
char& operator[](int i) { //Judge whether the position is out of bounds assert(i < _size); return _ptr[i]; } const char& operator[](int i) const { assert(i < _size); return _ptr[i]; }
6, string modification
(1) insert
1. Insert a single character
insert a character before the pos position;
when inserting, move the characters behind the position to be inserted from back to front and back in turn;
my_string& insert(size_t pos, char ch) { //Judge whether the access is out of bounds assert(pos <= _size); //Determine whether the container is full if (_size == _capacity) { size_t new_capacity = _capacity == 0 ? 15 : 2 * _capacity; reserve(new_capacity); } size_t end = _size; //Move element back and forward while (end > pos) { _ptr[end] = _ptr[end - 1]; end--; } _ptr[pos] = ch; _ptr[++_size] = '\0'; //Finally, you need to_ Add '\ 0' to the size position return *this; }
2. Insert string
insert a string before the pos position;
when inserting, move the characters behind the position to be inserted from back to front and back in turn;
my_string& insert(size_t pos, const char* str) { //Judge whether the boundary is crossed assert(pos <= _size); size_t len = strlen(str); //Determine whether the container is full if (_size + len > _capacity) { reserve(_size + len); } size_t end = _size + len - 1; while (end > pos + len - 1) { _ptr[end] = _ptr[end - len]; end--; } memcpy(_ptr + pos, str, len); _size += len; _ptr[_size] = '\0'; return *this; }
(2) Delete erase
delete the pos position n characters backward. pos defaults to 0 and len defaults to the maximum npos (that is, delete all from the pos position to the end of the string);
when deleting, move the elements behind the deleted elements from front to back;
my_string& erase(size_t pos = 0, size_t len = npos) { //Judge boundary assert(pos < _size); //Determine whether it is an invalid deletion location if ((pos + len >= _size) || (len == npos)) { _size = pos; _ptr[_size] = '\0'; return *this; } size_t start = pos + len; //Move position from front to back while (start < _size) { _ptr[start - len] = _ptr[start]; start++; } _size -= len; _ptr[_size] = '\0'; return *this; }
(3) Tail plug push_back
void push_back(char ch) { insert(_size, ch); }
(4) Tail deletion pop_back
void pop_back() { erase(_size - 1); }
(5) Append insert append
void append(const char* str) { insert(_size, str); }
(6) + = operator overload
+ = the operation will change the content of the original object, so the reference is returned
my_string& operator+=(const my_string& str) { append(str._ptr); return *this; }
7, Relational operator overloading
//< operator overload bool operator<(const my_string& str) { if (strcmp(_ptr, str._ptr) < 0) return true; return false; } //< = operator overload bool operator<=(const my_string& str) { if (*this > str) return false; return true; } //>Operator overloading bool operator>(const my_string& str) { if (strcmp(_ptr, str._ptr) > 0) return true; return false; } //>=Operator overloading bool operator>=(const my_string& str) { if (*this < str) return false; return true; } //==Operator overloading bool operator==(const my_string& str) { if (strcmp(_ptr, str._ptr) == 0) return true; return false; } //!= Operator overloading bool operator!=(const my_string& str) { if (*this == str) return false; return true; }
8, Search
1. Find a character
search for a character from the pos position. The default value of pos is 0;
because the string cannot be modified during search, const needs to be used to modify the function;
returns the position where c first appears in a string
// Returns the first occurrence of c in a string size_t find(char ch, size_t pos = 0) const { size_t count = 0; while (count < _size) { if (*(_ptr + pos + count) == ch) return count; count++; } return count; }
2. Find a string
search a string from pos position. The default value of pos is 0;
because the string cannot be modified during search, const needs to be used to modify the function;
returns the position where the substring s first appears in the string
// Returns the position where the substring s first appears in the string size_t find(const char* str, size_t pos = 0) const { size_t len = strlen(str); size_t count = 0; while (count < _size - len + 1) { size_t i = pos + count; while (i < count + len) { size_t j = 0; if (_ptr[i] != str[j]) break; i++; j++; } if (i == count + len) return count; count++; } return count; }
9, Input / output operator overload (not a member function)
input / output operator overloaded function is not a member function, do not write my_string class;
(1) Input operator overload
istream& operator>>(istream& _cin, my_string& str) { char ch; while ((ch = getchar()) != EOF) { if (ch == '\n') return _cin; str += ch; } return _cin; }
(2) Output operator overload
ostream& operator<<(ostream& _cout, const my_string& str) { for (const auto& ch : str) { _cout << ch; } return _cout; }