【C++】—— string模拟实现

前言:

学习了string的使用,总感觉了解不是很深厚;自己模拟实现string类来帮助自己理解。

这里只是实现了一部分内容(并没有实现完整的string类)。

先来实现string类里面的成员变量:

cpp 复制代码
#include<iostream>
namespace HL
{
	class string
	{
    public:
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
		const static size_t npos;
	};

	const size_t HL::string::npos = -1;
}

一、string默认成员函数(构造、析构、赋值运算符重载)

1.1、构造函数

1> 默认构造

默认构造函数就是不需要传参的构造函数;这里实现就开辟一个字符的空间存放 '\0'即可(_capacity不包括 '\0' )。

cpp 复制代码
string()
		{
			_str = new char[1];
			_str[0] = '\0';
			_size = 0;
			_capacity = 0;
		}

2> 拷贝构造

拷贝构造,在实现时需要注意:是深拷贝,而不是浅拷贝(值拷贝)。

深拷贝(深拷贝简单来说就是,要开辟一块新的空间,把原空间里的值拷贝到新的空间里)。

cpp 复制代码
		string(const string& str)
		{
			_str = new char[str._capacity + 1];
			memcpy(_str, str._str, str._size + 1);
			_size = str._size;
			_capacity = str._capacity;
		}

3> 其他构造

其他构造函数就有很多了,这里就实现以下这几个:

string (const char* s);

cpp 复制代码
		string(const char* s)
		{
			size_t len = strlen(s);
			_str = new char[len + 1];
			memcpy(_str, s, len + 1);
			_size = len;
			_capacity = len;
		}

string (const char* s, size_t n);

cpp 复制代码
		string(const char* s, size_t n)
		{
			size_t len = strlen(s);
			if (n > len)
			{
				n = len;
			}
			_str = new char[n + 1];
			memcpy(_str, s, n);
			_str[n] = '\0';
			_size = n;
			_capacity = n;
		}

string (size_t n, char c);

cpp 复制代码
		string(size_t n, char c)
		{
			_str = new char[n + 1];
			for (size_t i = 0; i < n; i++)
			{
				_str[i] = c;
			}
			_str[n] = '\0';
			_size = n;
			_capacity = n;
		}

1.2、析构函数

析构函数比较简单,释放开辟的资源即可;

cpp 复制代码
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;

		}

1.3、赋值运算符重载

赋值运算符有3个重载,这里就一一实现:

string& operator= (const string& str );

实现这个有很多种方法,

可以释放原空间,再开辟新的空间,将数据拷贝到新的空间中去

cpp 复制代码
		string& operator=(const string& str)
		{
			delete[] _str;
			_str = new char[str._capacity];
			memcpy(_str, str._str, str._size + 1);
			_size = str._size;
			_capacity = str._capacity;
			return *this;
		}

可以调用拷贝构造,构造一个tmp、再将tmp与*this 中的值进行交换(要实现交换函数)

cpp 复制代码
template <typename T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}
string& operator= (const string& str)
{
    string tmp(str);
    Swap(_str, tmp._str);
    Swap(_size, tmp._size);
    Swap(_capacity, tmp._capacity);
    return *this;
}

这里如果已经实现string类swap成员函数,就可以直接调用。

string& operator= (const char* s );

cpp 复制代码
		/*string& operator= (const char* s)
		{
			size_t len = strlen(s);
			delete[] _str;
			_str = new char[len + 1];
			memcpy(_str, s, len + 1);
			_size = _capacity = len;

			return *this;
		}*/
		string& operator= (const char* s)
		{
			string tmp(s);
			Swap(_str, tmp._str);
			Swap(_size, tmp._size);
			Swap(_capacity, tmp._capacity);
			return *this;
		}

string& operator= (char c );

cpp 复制代码
		string& operator= (char c)
		{
			delete[] _str;
			_str = new char[2];
			_str[0] = c;
			_str[1] = '\0';
			_size = _capacity = 1;
			return *this;
		}

二、元素访问与迭代器

2.1、迭代器

迭代器,虽然在string类中使用的不是很多,但在后面的容器中有大用处。

(在string类中就可以简单的理解成指针)。

cpp 复制代码
		//迭代器
		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);
		}

实现了迭代器之后,范围for这个语法糖就可以使用了(底层就是迭代器)。

2.2、下标访问元素

实现下标访问,就是 [ ]运算符重载。

cpp 复制代码
		//下标访问 [ ]
		char& operator[] (size_t pos)
		{
			assert(pos >= _size);
			return *(_str + pos);
		}
		const char& operator[] (size_t pos) const
		{
			assert(pos >= _size);
			return *(_str + _size);
		}

at函数和 [ ] 运算符重载原理一样,这里就不重复写了。

三、增删查改

在实现增之前,要先实现一个函数,就是调整空间大小的(扩容来用)。

cpp 复制代码
		//扩容
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* s = new char[n + 1];
				memcpy(s, _str, _size);
				delete[] _str;
				_str = s;
				_capacity = n;
			}
		}

增删这里就实现这些成员函数。

1、push_back 、append 、operator+=

append重载比较多,这里就实现其中的几个。

cpp 复制代码
		//扩容
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* s = new char[n + 1];
				memcpy(s, _str, _size);
				delete[] _str;
				_str = s;
				_capacity = n;
			}
		}

		//增
		void push_back(char c)
		{
			if (_size >= _capacity)
			{
				reserve((_capacity == 0) ? 4 : 2 * _capacity);
			}
			_str[_size] = c;
			_size++;
			_str[_size] = '\0';
		}
		void append(const string& str)
		{
			size_t n = _size + str._size;
			if (n > _capacity)
			{
				reserve(n);
			}
			for (int i = 0; i < str._size; i++)
			{
				_str[_size + i] = str._str[i];
			}
			_size += str._size;
			_str[_size] = '\0';
		}
		void append(const char* s)
		{
			size_t len = strlen(s);
			int n = _size + len;
			if (n > _capacity)
			{
				reserve(n);
			}
			for (int i = 0; i < len; i++)
			{
				_str[_size + i] = s[i];
			}
			_size = n;
			_str[_size] = '\0';
		}
		void append(size_t n, char c)
		{
			if (_size + n > _capacity)
			{
				reserve(_size + n);
			}
			for (int i = 0; i < n; i++)
			{
				_str[_size + i] = c;
			}
			_size += n;
			_str[_size] = '\0';
		}
		string& operator+=(const string& str)
		{
			this->append(str);
			return *this;
		}
		string& operator+=(const char* s)
		{
			this->append(s);
			return *this;
		}
		string& operator+=(char c)
		{
			this->push_back(c);
			return *this;
		}

2、insert、erase

insert重载也比较多,比较冗余;这里也只实现其中的一部分;

erase这里只实现一个

cpp 复制代码
//insert 、erase
void insert(size_t pos, const string& str)
{
	assert(pos >= 0 && pos < _size);
	size_t len = str._size;
	size_t n = _size + len;
	if (n > _capacity)
	{
		reserve(n);
	}
	//挪动数据
	for (size_t i = n; i >= pos + len; i--)
	{
		_str[i] = _str[i - len];
	}
	memcpy(_str + pos, str._str, str._size);
	_size += str._size;
	_str[_size] = '\0';
}
void insert(size_t pos, const char* s)
{
	assert(pos >= 0 && pos < _size);
	size_t len = strlen(s);
	size_t n = _size + len;
	if (n > _capacity)
	{
		reserve(n);
	}
	//挪动数据
	for (size_t i = n; i >= pos + len; i--)
	{
		_str[i] = _str[i - len];
	}
	memcpy(_str + pos, s, len);
	_size += len;
	_str[_size] = '\0';
}
void insert(size_t pos, size_t n, char c)
{
	assert(pos >= 0 && pos < _size);
	if (_size + n > _capacity)
	{
		reserve(_size + n);
	}
	//挪动数据
	for (size_t i = _size+n; i >= pos + n; i--)
	{
		_str[i] = _str[i - n];
	}
	for (size_t i = 0; i < n; i++)
	{
		_str[pos + i] = c;
	}
	_size += n;
	_str[_size] = '\0';
}
void erase(size_t pos, size_t len = npos)
{
	assert(pos >= 0 && pos < _size);
	if (len == npos)
	{
		_str[0] = '\0';
		_size = 0;
		return;
	}
	for (size_t i = pos; (len + i) < _size; i++)
	{
		_str[i] = _str[i + len];
	}
	_size -= len;
	_str[_size] = '\0';
}

3、find

cpp 复制代码
		//find
		size_t find(const string& str, size_t pos = 0)
		{
			assert(pos >= 0 && pos < _size);
			char* tmp = strstr(_str + pos, str._str);
			if (tmp == nullptr)
			{
				return -1;
			}
			return tmp - _str;
		}
		size_t find(const char* s, size_t pos = 0)
		{
			assert(pos >= 0 && pos < _size);
			char* tmp = strstr(_str + pos, s);
			if (tmp == nullptr)
			{
				return -1;
			}
			return tmp - _str;
		}
		size_t find(char c, size_t pos = 0)
		{
			assert(pos >= 0 && pos < _size);
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == c)
				{
					return i;
				}
			}
			return -1;
		}

4、swap

swap作为string的成员函数,交换两个string类类型的对象。

cpp 复制代码
		//swap
		void swap(string& str)
		{
			Swap(_str, str._str);
			Swap(_size, str._size);
			Swap(_capacity, str._capacity);
		}

这里swap函数内部也可以调用库里面的swap模版(这里我自己写了一个模版Swap)

有了swap函数,上面赋值运算符重载中就可以这样写了:

cpp 复制代码
		string& operator= (const string& str)
		{
			string tmp(str);
			swap(tmp);
			return *this;
		}

四、字符串操作函数

1、c_str

c_str()函数返回string类对象中的字符串;

cpp 复制代码
const char* c_str() const
{
	return _str;
}	
char* c_str()
{
	return _str;
}

2、substr

获得,sting对象中字符串的子串。

cpp 复制代码
//substr
string substr(size_t pos = 0, size_t len = npos) const
{
	assert(pos >= 0 && pos < _size);
	size_t n = 0;
	if (len == npos || pos + len > _size)
	{
		n = _size - pos;
	}
	else
	{
		n = len;
	}
	string ret;
	for (size_t i = 0; i < n; i++)
	{
		ret += _str[pos + i];
	}
	return ret;
}

五、其他成员函数

这里max_size是返回容器可以容纳的最大元素的数量,这里就不进行实现了。

reserve在增加元素前已经实现了。(扩容)

cpp 复制代码
//其他成员函数
size_t size() const
{
	return _size;
}
size_t length()const
{
	return _size;
}
size_t capacity()const
{
	return _capacity;
}
void clear()
{
	_str[0] = '\0';
	_size = 0;
}
bool empty()const
{
	return _size == 0;
}
void resize(size_t n)
{
	if (n > _capacity)
	{
		reserve(n);
	}
	_size = n;
	_str[_size] = '\0';
}
void resize(size_t n, char c)
{
	if (n > _capacity)
	{
		reserve(n);
	}
	for (size_t i = _size; i < n; i++)
	{
		_str[i] = c;
	}
	_size = n;
	_str[_size] = '\0';
}

六、流插入、流提取

因为成员函数有一个隐藏的this指针,会和istream 和istream 类对象抢占第一个参数的位置,所以我们不能将流插入和流提取写成string类的成员函数。

string中实现了访问元素方成员函数,就可以不将流插入、流提取写成string类的友元函数。

1、流插入运算符重载

cpp 复制代码
	std::ostream& operator<<(std::ostream& out, const HL::string& str)
	{
		//for (int i = 0; i < str.size(); i++)
		//{
		//	out << str[i];
		//}
		//return out;
		for (auto ch : str)
		{
			out << ch;
		}
		return out;
	}

2、流提取运算符重载

1、 s.clear()清理缓冲区(上次cin流提取的剩余);

2、 创建一个数组,防止多次去开空间(输入到128或者输入结束(' '或者'\n')才添加到str中)。

3、下面的代码处理缓冲区的空格。

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

4、最后循环里if是遇到空格或者换行结束,将s中输入添加到s中,末尾添加'\0'。

cpp 复制代码
std::istream& operator>>(std::istream& in, HL::string& str)
{
	char s[128] = { 0 };
	char ch;
	ch = in.get();
	while (ch == ' ' || ch == '\n')
	{
		ch = in.get();
	}
	str.clear();
	int i = 0;
	while (ch != '\n')
	{
		s[i] = ch;
		i++;
		if (i == 127)
		{
			s[i] = '\0';
			str += s;
			i = 0;
		}
		ch = in.get();
	}
	if (i)
	{
		str += s;
	}
	return in;
}

HL :: string 源码

cpp 复制代码
#pragma once
#include<iostream>
#include<cassert>

template <typename T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}
namespace HL
{
	class string
	{
		friend std::ostream& operator<<(std::ostream& out, const HL::string& str);
		friend std::istream& operator>>(std::istream& in, HL::string& str);
	public:
		//构造函数
		/*string()
		{
			_str = new char[1];
			_str[0] = '\0';
			_size = 0;
			_capacity = 0;
		}
		
		string(char ch)
		{
			_str = new char[1];
			_str[0] = ch;
			_size = 0;
			_capacity = 0;
		}*/
		string(char ch = '\0')
		{
			_str = new char[2];
			_str[0] = ch;
			_str[1] = '\0';
			_size = 1;
			_capacity = 1;
		}
		string(const char* s)
		{
			size_t len = strlen(s);
			_str = new char[len + 1];
			memcpy(_str, s, len + 1);
			_size = len;
			_capacity = len;
		}
		string(const char* s, size_t n)
		{
			size_t len = strlen(s);
			if (n > len)
			{
				n = len;
			}
			_str = new char[n + 1];
			memcpy(_str, s, n);
			_str[n] = '\0';
			_size = n;
			_capacity = n;
		}
		string(const string& str)
		{
			_str = new char[str._capacity + 1];
			memcpy(_str, str._str, str._size + 1);
			_size = str._size;
			_capacity = str._capacity;
		}
		string(size_t n, char c)
		{
			_str = new char[n + 1];
			for (size_t i = 0; i < n; i++)
			{
				_str[i] = c;
			}
			_str[n] = '\0';
			_size = n;
			_capacity = n;
		}

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

		/*string& operator=(const string& str)
		{
			delete[] _str;
			_str = new char[str._capacity];
			memcpy(_str, str._str, str._size + 1);
			_size = str._size;
			_capacity = str._capacity;
			return *this;
		}*/
		/*string& operator= (const string& str)
		{
			string tmp(str);
			Swap(_str, tmp._str);
			Swap(_size, tmp._size);
			Swap(_capacity, tmp._capacity);
			return *this;
		}*/
		string& operator= (const string& str)
		{
			string tmp(str);
			swap(tmp);
			return *this;
		}
		/*string& operator= (const char* s)
		{
			size_t len = strlen(s);
			delete[] _str;
			_str = new char[len + 1];
			memcpy(_str, s, len + 1);
			_size = _capacity = len;

			return *this;
		}*/
		string& operator= (const char* s)
		{
			string tmp(s);
			Swap(_str, tmp._str);
			Swap(_size, tmp._size);
			Swap(_capacity, tmp._capacity);
			return *this;
		}
		string& operator= (char c)
		{
			delete[] _str;
			_str = new char[2];
			_str[0] = c;
			_str[1] = '\0';
			_size = _capacity = 1;
			return *this;
		}
		//string& operator= (char c)
		//{
		//	string tmp(c);
		//	Swap(_str, tmp._str);
		//	Swap(_size, tmp._size);
		//	Swap(_capacity, tmp._capacity);
		//	return *this;
		//}

		//迭代器
		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);
		}

		//下标访问 [ ]
		char& operator[] (size_t pos)
		{
			assert(pos >= 0 && pos < _size);
			return *(_str + pos);
		}
		const char& operator[] (size_t pos) const
		{
			assert(pos >= 0 && pos < _size);
			return *(_str + _size);
		}

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

		//增
		void push_back(char c)
		{
			if (_size >= _capacity)
			{
				reserve((_capacity == 0) ? 4 : 2 * _capacity);
			}
			_str[_size] = c;
			_size++;
			_str[_size] = '\0';
		}
		void append(const string& str)
		{
			size_t n = _size + str._size;
			if (n > _capacity)
			{
				reserve(n);
			}
			for (int i = 0; i < str._size; i++)
			{
				_str[_size + i] = str._str[i];
			}
			_size += str._size;
			_str[_size] = '\0';
		}
		void append(const char* s)
		{
			size_t len = strlen(s);
			size_t n = _size + len;
			if (n > _capacity)
			{
				reserve(n);
			}
			for (int i = 0; i < len; i++)
			{
				_str[_size + i] = s[i];
			}
			_size = n;
			_str[_size] = '\0';
		}
		void append(size_t n, char c)
		{
			if (_size + n > _capacity)
			{
				reserve(_size + n);
			}
			for (int i = 0; i < n; i++)
			{
				_str[_size + i] = c;
			}
			_size += n;
			_str[_size] = '\0';
		}
		string& operator+=(const string& str)
		{
			this->append(str);
			return *this;
		}
		string& operator+=(const char* s)
		{
			this->append(s);
			return *this;
		}
		string& operator+=(char c)
		{
			this->push_back(c);
			return *this;
		}

		//insert 、erase
		void insert(size_t pos, const string& str)
		{
			assert(pos >= 0 && pos < _size);
			size_t len = str._size;
			size_t n = _size + len;
			if (n > _capacity)
			{
				reserve(n);
			}
			//挪动数据
			for (size_t i = n; i >= pos + len; i--)
			{
				_str[i] = _str[i - len];
			}
			memcpy(_str + pos, str._str, str._size);
			_size += str._size;
			_str[_size] = '\0';
		}
		void insert(size_t pos, const char* s)
		{
			assert(pos >= 0 && pos < _size);
			size_t len = strlen(s);
			size_t n = _size + len;
			if (n > _capacity)
			{
				reserve(n);
			}
			//挪动数据
			for (size_t i = n; i >= pos + len; i--)
			{
				_str[i] = _str[i - len];
			}
			memcpy(_str + pos, s, len);
			_size += len;
			_str[_size] = '\0';
		}
		void insert(size_t pos, size_t n, char c)
		{
			assert(pos >= 0 && pos < _size);
			if (_size + n > _capacity)
			{
				reserve(_size + n);
			}
			//挪动数据
			for (size_t i = _size+n; i >= pos + n; i--)
			{
				_str[i] = _str[i - n];
			}
			for (size_t i = 0; i < n; i++)
			{
				_str[pos + i] = c;
			}
			_size += n;
			_str[_size] = '\0';
		}
		void erase(size_t pos, size_t len = npos)
		{
			assert(pos >= 0 && pos < _size);
			if (len == npos)
			{
				_str[0] = '\0';
				_size = 0;
				return;
			}
			for (size_t i = pos; (len + i) < _size; i++)
			{
				_str[i] = _str[i + len];
			}
			_size -= len;
			_str[_size] = '\0';
		}

		//find
		size_t find(const string& str, size_t pos = 0)
		{
			assert(pos >= 0 && pos < _size);
			char* tmp = strstr(_str + pos, str._str);
			if (tmp == nullptr)
			{
				return -1;
			}
			return tmp - _str;
		}
		size_t find(const char* s, size_t pos = 0)
		{
			assert(pos >= 0 && pos < _size);
			char* tmp = strstr(_str + pos, s);
			if (tmp == nullptr)
			{
				return -1;
			}
			return tmp - _str;
		}
		size_t find(char c, size_t pos = 0)
		{
			assert(pos >= 0 && pos < _size);
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == c)
				{
					return i;
				}
			}
			return -1;
		}
		
		//swap
		void swap(string& str)
		{
			Swap(_str, str._str);
			Swap(_size, str._size);
			Swap(_capacity, str._capacity);
		}

		//c_str
		const char* c_str() const
		{
			return _str;
		}	
		char* c_str()
		{
			return _str;
		}
		
		//substr
		string substr(size_t pos = 0, size_t len = npos) const
		{
			assert(pos >= 0 && pos < _size);
			size_t n = 0;
			if (len == npos || pos + len > _size)
			{
				n = _size - pos;
			}
			else
			{
				n = len;
			}
			string ret;
			for (size_t i = 0; i < n; i++)
			{
				ret += _str[pos + i];
			}
			return ret;
		}

		//其他成员函数
		size_t size() const
		{
			return _size;
		}
		size_t length()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _capacity;
		}
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		bool empty()const
		{
			return _size == 0;
		}
		void resize(size_t n)
		{
			if (n > _capacity)
			{
				reserve(n);
			}
			_size = n;
			_str[_size] = '\0';
		}
		void resize(size_t n, char c)
		{
			if (n > _capacity)
			{
				reserve(n);
			}
			for (size_t i = _size; i < n; i++)
			{
				_str[i] = c;
			}
			_size = n;
			_str[_size] = '\0';
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
		const static size_t npos;
	};

	const size_t HL::string::npos = -1;

	std::ostream& operator<<(std::ostream& out, const HL::string& str)
	{
		//for (int i = 0; i < str.size(); i++)
		//{
		//	out << str[i];
		//}
		//return out;
		for (auto ch : str)
		{
			out << ch;
		}
		return out;
	}
	std::istream& operator>>(std::istream& in, HL::string& str)
	{
		char s[128] = { 0 };
		char ch;
		ch = in.get();
		while (ch == ' ' || ch == '\n')
		{
			ch = in.get();
		}
		str.clear();
		int i = 0;
		while (ch != '\n')
		{
			s[i] = ch;
			i++;
			if (i == 127)
			{
				s[i] = '\0';
				str += s;
				i = 0;
			}
			ch = in.get();
		}
		if (i)
		{
			str += s;
		}
		return in;
	}
};
相关推荐
唤醒手腕4 分钟前
2024年最新 Python 大数据网络爬虫技术基础案例详细教程(更新中)
开发语言·爬虫·python
秦淮渔火11 分钟前
单例模式(饿汉式-懒汉式)
java·开发语言·单例模式
蔚一13 分钟前
Java设计模式(单例模式)——单例模式存在的问题(完整详解,附有代码+案例)
java·开发语言·单例模式·设计模式
zhangbin_23718 分钟前
【Python机器学习】NLP信息提取——正则模式
开发语言·人工智能·python·深度学习·机器学习·自然语言处理
ღ᭄陽先生꧔ꦿ᭄29 分钟前
Java异常架构与异常关键字
java·开发语言·架构
郭源潮130 分钟前
【C++二叉树】二叉树的前序遍历、中序遍历、后序遍历递归与非递归实现
开发语言·c++·算法
繁依Fanyi36 分钟前
【问题随记】在使用 AuthenticationManager 的时候,出现循环依赖问题 —— `java.lang.StackOverflowError`
java·大数据·开发语言
咩咩大主教42 分钟前
LinuxC++的UDP服务器和客户端通信
linux·服务器·c语言·开发语言·c++·udp
zyx没烦恼42 分钟前
C\C++内存管理详解
c语言·c++
Qhumaing44 分钟前
C语言PTA-计算最大游戏胜率
c语言·开发语言·学习·pta