C + + -- analog implementation of string

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;
}

Tags: C++

Posted by David-fethiye on Sun, 08 May 2022 06:12:56 +0300