[C++] STL——string simulation implementation, common constructors, iterators, operator overloading, expansion functions, additions, deletions, checks, and modifications

1. Simulate the realization of string

string usage article
insert image description here

1.1 Constructor

  Here we implement the commonly used fourth string (const char* s) and destructor

insert image description here
insert image description here

class string {
    
    
public:
	//初始化列表赋值
	//string(const char* str = "\0")
	//	:_size(strlen(str))
	//	,_capacity(_size)
	//	,_str(new char[_capacity+1])
	//{
    
    
	//	memcpy(_str, str, _size + 1);
	//}
	
	//函数体内赋值
    string(const char* str = "\0") 
    {
    
    
        _size = strlen(str); // 获取字符串长度
        _capacity = _size; // 设置容量为长度
        _str = new char[_capacity + 1]; // 分配内存空间
        memcpy(_str, str, _size + 1); // 复制字符串内容
       	//strcpy(_str, str);
       	//这里最好不要使用strcpy,因为strcpy是C语言中的函数
       	//在检测到/0直接结束,若字符串中有/0,会导致部分字符串无法复制
    }

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

private:
    int _size; // 字符串长度
    int _capacity; // 字符串容量
    char* _str; // 字符串内容
};


  Copy constructor implementation:

insert image description here

  Use the new keyword on the heap to allocate memory space for the member variable _str of the current object, with a size of s._capacity + 1 byte, that is, the capacity of the string plus the space for a terminator \0.

  We use deep copy instead of shallow copy. Shallow copy will cause the pointers of two objects to point to the same memory space. When one of the objects is destroyed, the memory space will be released, causing the memory pointed to by the other object to become a dangling pointer. Therefore, a deep copy is used in the copy constructor to ensure that each object has an independent memory space to store the string content, avoiding the problems of dangling pointers and program crashes.

string(const string& s)
{
    
    
	this->_size = s._size;
	this->_capacity = s._capacity;
	
	//浅拷贝,两个指针指向同一块空间,且调用两次析构函数,程序崩溃
	//this->_str = s._str;

	//深拷贝 实现
	this->_str = new char[s._capacity + 1];
	//strcpy(_str, s.c_str());
	memcpy(_str, s.c_str(), _size + 1);
}

1.2 Iterators

insert image description here
insert image description here

  We access each character of the string through an iterator. The begin function returns an iterator pointing to the beginning of the string, and the end function returns an iterator pointing to the end of the string . This way, the string can be traversed using standard iterator operations, such as using a loop to iterate through each character.

  For the ordinary string type, the compiler calls the iterators of the above two types of iterator, indicating that the string can be read and written; for the const type of string, the compiler calls the following two iterators of the type const_iterator, Indicates that the string has only write permission.

//实现迭代器和const迭代器
typedef char* iterator;
typedef const char* const_iterator;

iterator begin()
{
    
    
	return _str;
}

iterator end()
{
    
    
	return _str + _size;
}

const_iterator begin() const
{
    
    
	return _str;
}

const_iterator end() const
{
    
    
	return _str + _size;
}

1.3 Operator overloading

operator[]

insert image description here
  Similar to the iterator implementation above, we also need to implement two operator[].

  The overloaded normal version of the operator[] function accepts a positional parameter pos of type size_t. It first uses the assert assertion to check whether the specified position is within a valid range, ie less than the length _size of the string. It then returns _str[pos], which is a reference to the character at the specified position pos in the string. Because the function returns a reference, the character can be modified through the reference.

  The overloaded const version of the operator[] function is similar to the version, but the function itself is declared as a const member function to ensure that the object's member variables are not modified. This means that operator[] can only read permissions on const objects, but cannot modify characters through the returned reference.

  By overloading the [] operator, you can access the characters in the class as conveniently as manipulating an array. For example, for the string object str, you can use str[0] to access the first character, str[1] to access the second character, and so on.

//重载[]操作符
char& operator[](size_t pos)
{
    
    
	assert(pos < _size);

	return _str[pos];
}
 
//重载const[]操作符
const char& operator[](size_t pos) const
{
    
    
	assert(pos < _size);

	return _str[pos];
}

assignment operator

insert image description here

insert image description here

  The assignment operator overloading function in the traditional writing method first makes a self-assignment judgment to ensure that no problems will occur in the case of self-assignment. Then, create a temporary character array tmp with a size of s._capacity + 1, and use the memcpy function to copy the contents of s._str to tmp. Next, delete the memory space pointed to by _str of the current object, and point its pointer to the newly allocated tmp. Finally, update the _size and _capacity of the current object.

  The assignment operator overloading function in modern writing uses a copy constructor to simplify the implementation. It first creates a temporary string object tmp, and initializes tmp with the parameter s. Then, call the swap function to exchange the member variables of the current object with the member variables of the tmp object. The advantage of this is that by exchanging pointers, manual memory allocation and release can be avoided, and exception safety is guaranteed. Finally, a reference to the current object is returned.

//传统写法
/*string& operator=(const string& s)
{
	if (this != &s)
	{
		char* tmp = new char[s._capacity + 1];
		memcpy(tmp, s._str, s._size + 1);
		delete[] _str;
		_str = tmp;

		_size = s._size;
		_capacity = s._capacity;
	}

	return *this;
}*/

//现代写法
void swap(string& s)
{
    
    
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}

string& operator=(const string& s)
{
    
    
	if (this != &s)
	{
    
    
		string tmp(s);

		//this->swap(tmp);
		swap(tmp);
	}

	return *this;
}

//更简单的写法
//string& operator=(string tmp)
//{
    
    
//	swap(tmp);
//
//	return *this;
//}

Other operator overloads for string comparison:

bool operator<(const string& s) const
{
    
    
	int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
	return ret == 0 ? _size < s._size : ret < 0;
}

bool operator==(const string& s) const
{
    
    
	return _size == s._size
	&& memcmp(_str, s._str, _size) == 0;
}

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

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

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

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

1.4 Expansion function

reserve and resize

insert image description here
insert image description here

  The role of these two functions is to ensure that the string object has sufficient capacity and the correct size when the string needs to be expanded or resized. Among them, the reserve function is used to expand the capacity, and the resize function is used to adjust the size, and perform memory allocation and character filling when necessary.

//扩容capacity
void reserve(size_t n)
{
    
    
	if (n >= _capacity)
	{
    
    
		char* tmp = new char[n + 1];
		//strcpy(tmp,_str);
		memcpy(tmp, _str, _size + 1);

		delete[] _str;
		_str = tmp;
		_capacity = n;
	}
}

//扩容size
void resize(size_t n, char ch = '\0')
{
    
    
	if (n < _size)
	{
    
    
		_size = n;
		_str[_size] = '\0';
	}
	else
	{
    
    
		reserve(n);

		for (size_t i = _size; i < n; i++)
		{
    
    
			_str[i] = ch;
		}

		_size = n;
		_str[_size] = '\0';
	}
}

1.5 Add, delete, check and modify

push_back and append and += operator overloading

insert image description here
insert image description here

insert image description here

  What these functions do is add a character or an array of characters to a string object. Among them, push_back can be used to add characters to the end of the string one by one, append can append character arrays to the string, and operator+= operator overloading provides a convenient way to connect strings and characters.

//实现尾插
void push_back(const char ch)
{
    
    
	//先扩容
	if (_size == _capacity)
	{
    
    
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	this->_str[_size] = ch;

	++_size;
	this->_str[_size] = '\0';
}

//实现append
void append(const char* str)
{
    
    
	size_t len = strlen(str);
	if (_size + len > _capacity)
	{
    
    
		//至少要扩容到_size+len
		reserve(_size + len);
	}

	//strcpy(_str + _size, str);
	memcpy(_str + _size, str, len + 1);
	_size += len;
}

//复用实现+=的运算符重载
string& operator+=(const char ch)
{
    
    
	push_back(ch);
	return *this;
}


Complete implementation (including insert, erase, find, substr, operator<<, operator>>)

#include<assert.h>

namespace str
{
    
    
	class string
	{
    
    
	public:
		//实现迭代器和const迭代器
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin()
		{
    
    
			return _str;
		}

		iterator end()
		{
    
    
			return _str + _size;
		}

		const_iterator begin() const
		{
    
    
			return _str;
		}

		const_iterator end() const
		{
    
    
			return _str + _size;
		}

		构造函数为空值
		//string()
		//	:_size(0)
		//	, _capacity(_size)
		//	, _str(new char[1])
		//{
    
    
		//	_str[0] = '\0';
		//}

		构造函数传字符串
		//string(const char* str)
		//	:_size(strlen(str))
		//	,_capacity(_size)
		//	,_str(new char[_capacity+1])
		//{
    
    
		//	memcpy(_str, str, _size + 1);
		//}

		//合并string()和string(const char* str)
		//并且在函数体内赋值
		string(const char* str = "\0")//->"\0"\0
		{
    
    
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			//为空时,实参未显示赋值,我们给str缺省参数"\0"
			//str传给_str"\0",系统设为空
			//"\0"为字符串以指针传递、'\0'为单个字符以单个字符传递
			//strcpy(_str, str);
			memcpy(_str, str, _size + 1);
		}

		string(const string& s)
		{
    
    
			this->_size = s._size;
			this->_capacity = s._capacity;
			
			//浅拷贝,两个指针指向同一块空间,且调用两次析构函数,程序崩溃
			//this->_str = s._str;

			//深拷贝 实现
			this->_str = new char[s._capacity + 1];
			//strcpy(_str, s.c_str());
			memcpy(_str, s.c_str(), _size + 1);
		}

		//传统写法
		/*string& operator=(const string& s)
		{
			if (this != &s)
			{
				char* tmp = new char[s._capacity + 1];
				memcpy(tmp, s._str, s._size + 1);
				delete[] _str;
				_str = tmp;

				_size = s._size;
				_capacity = s._capacity;
			}

			return *this;
		}*/

		//现代写法
		void swap(string& s)
		{
    
    
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		//string& operator=(const string& s)
		//{
    
    
		//	if (this != &s)
		//	{
    
    
		//		string tmp(s);
		//
		//		//this->swap(tmp);
		//		swap(tmp);
		//	}
		//
		//	return *this;
		//}

		string& operator=(string tmp)
		{
    
    
			swap(tmp);

			return *this;
		}

		//析构函数
		~string()
		{
    
    
			_size = 0;
			_capacity = 0;
			delete[] _str;
			_str = nullptr;
		}

		//const返回c类型的字符串
		const char* c_str() const
		{
    
    
			return _str;
		}

		//const返回str的大小
		size_t size() const
		{
    
    
			return _size;
		}

		//重载[]操作符
		char& operator[](size_t pos)
		{
    
    
			assert(pos < _size);

			return _str[pos];
		}
		 
		//重载const[]操作符
		const char& operator[](size_t pos) const
		{
    
    
			assert(pos < _size);

			return _str[pos];
		}

		//扩容函数capacity
		void reserve(size_t n)
		{
    
    
			if (n >= _capacity)
			{
    
    
				char* tmp = new char[n + 1];
				//strcpy(tmp,_str);
				memcpy(tmp, _str, _size + 1);

				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}

		//扩容size
		void resize(size_t n, char ch = '\0')
		{
    
    
			if (n < _size)
			{
    
    
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
    
    
				reserve(n);

				for (size_t i = _size; i < n; i++)
				{
    
    
					_str[i] = ch;
				}

				_size = n;
				_str[_size] = '\0';
			}
		}

		//实现尾插
		void push_back(const char ch)
		{
    
    
			//先扩容
			if (_size == _capacity)
			{
    
    
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			this->_str[_size] = ch;

			++_size;
			this->_str[_size] = '\0';
		}

		//实现append
		void append(const char* str)
		{
    
    
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
    
    
				//至少要扩容到_size+len
				reserve(_size + len);
			}

			//strcpy(_str + _size, str);
			memcpy(_str + _size, str, len + 1);
			_size += len;
		}

		//复用实现+=的运算符重载
		string& operator+=(const char ch)
		{
    
    
			push_back(ch);
			return *this;
		}

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

		void insert(size_t pos, size_t n, char ch)
		{
    
    
			assert(pos <= _size);

			if (_size + n > _capacity)
			{
    
    
				reserve(_size + n);
			}

			size_t end = _size;
			while (end >= pos && end != npos)
			{
    
    
				_str[end + n] = _str[end];
				--end;
			}

			for (size_t i = 0; i < n; i++)
			{
    
    
				_str[pos + i] = ch;
			}

			_size += n;
		}

		void insert(size_t pos, const char* str)
		{
    
    
			assert(pos <= _size);

			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
    
    
				// 至少扩容到_size + len
				reserve(_size + len);
			}

			size_t end = _size;
			while (end >= pos && end != npos)
			{
    
    
				_str[end + len] = _str[end];
				--end;
			}

			for (size_t i = 0; i < len; i++)
			{
    
    
				_str[pos + i] = str[i];
			}

			_size += len;
		}

		void erase(size_t pos, size_t len = npos)
		{
    
    
			assert(pos <= _size);

			if (len == npos || pos + len >= _size)
			{
    
    
				//_str[pos] = '\0';
				_size = pos;

				_str[_size] = '\0';
			}
			else
			{
    
    
				size_t end = pos + len;
				while (end <= _size)
				{
    
    
					_str[pos++] = _str[end++];
				}
				_size -= len;
			}
		}

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

			const char* ptr = strstr(_str + pos, str);
			if (ptr)
			{
    
    
				return ptr - _str;
			}
			else
			{
    
    
				return npos;
			}
		}

		string substr(size_t pos = 0, size_t len = npos)
		{
    
    
			assert(pos < _size);

			size_t n = len;
			if (len == npos || pos + len > _size)
			{
    
    
				n = _size - pos;
			}

			string tmp;
			tmp.reserve(n);
			for (size_t i = pos; i < pos + n; i++)
			{
    
    
				tmp += _str[i];
			}

			return tmp;
		}

		void clear()
		{
    
    
			_str[0] = '\0';
			_size = 0;
		}

		//bool operator<(const string& s)
		//{
    
    
		//	size_t i1 = 0;
		//	size_t i2 = 0;
		//	while (i1 < _size && i2 < s._size)
		//	{
    
    
		//		if (_str[i1] < s._str[i2])
		//		{
    
    
		//			return true;
		//		}
		//		else if (_str[i1] > s._str[i2])
		//		{
    
    
		//			return false;
		//		}
		//		else
		//		{
    
    
		//			++i1;
		//			++i2;
		//		}
		//	}

		//	/*if (i1 == _size && i2 != s._size)
		//	{
    
    
		//		return true;
		//	}
		//	else
		//	{
    
    
		//		return false;
		//	}*/

		//	//return i1 == _size && i2 != s._size;
		//	return _size < s._size;
		//}

		bool operator<(const string& s) const
		{
    
    
			int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
			return ret == 0 ? _size < s._size : ret < 0;
		}

		bool operator==(const string& s) const
		{
    
    
			return _size == s._size
				&& memcmp(_str, s._str, _size) == 0;
		}

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

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

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

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

	private:
		int _size;
		int _capacity;
		char* _str;

	public:
		const static size_t npos;
	};

	const size_t string::npos = -1;

	ostream& operator<<(ostream& out, str::string& s)
	{
    
    
		//for (size_t i = 0; i < s.size(); i++)
		//{
    
    
		//	out << s[i];
		//}

		for (auto ch : s)
		{
    
    
			out << ch;
		}

		return out;
	}

	istream& operator>>(istream& in, std::string& s)
	{
    
    
		s.clear();

		char ch = in.get();
		while (ch == ' ' || ch == '\n')
		{
    
    
			ch = in.get();
		}

		//in >> ch;
		char buff[128];
		int i = 0;

		while (ch != ' ' && ch != '\n')
		{
    
    
			buff[i++] = ch;
			if (i == 127)
			{
    
    
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			//in >> ch;
			ch = in.get();
		}

		if (i != 0)
		{
    
    
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}
};

Guess you like

Origin blog.csdn.net/Crocodile1006/article/details/131948790
Recommended