string类模拟实现

发布于:2023-01-08 ⋅ 阅读:(137) ⋅ 点赞:(0)

🔎c++中string类是一个管理字符串的类,调用类中成员函数可对类里的字符串进行增删查改等功能,与C语言中操作字符串相比便捷了许多。下面我们来模拟实现一个类。


string类基本框架

#pragma once
#include<iostream>
using namespace std;

// 命名空间 -- 与库里面的string类进行隔离,防止冲突
namespace hyr
{
	// 管理字符串的数组,可以对字符串进行增删查改,字符串数组以'\0'结尾
	class string
	{
	private:
		char* _str;        // 储存字符串
		size_t _size;      // 字符串中有效字符个数
		size_t _capacity;  // 标识字符空间的大小

		static const size_t npos;
	};

	const size_t string::npos = -1;
}

string类的默认成员函数

1.构造函数

string(const char* str = "")
{
	// 构造string类对象,若传入了空指针,那么我们认为程序非法,我们用空字符串将其置空
	if (str == nullptr)
		str = "";

	_size = strlen(str);
	_capacity = _size;
	_str = new char[strlen(str) + 1]; //多开一个空间为存放'\0'
	strcpy(_str, str);
}

在这里插入图片描述
2.拷贝构造函数

🚀若我们没有显式写拷贝构造函数,编译器也会生成一个拷贝构造函数,但是编译器生成的只可以完成浅拷贝

浅拷贝:也叫做位拷贝,编译器只将对象中的值拷贝过来。若对象中管理资源,会导致多个对象共享一份资源,其中一个对象销毁就会释放掉该资源,另一些对象对该资源进行操作时,就会发生访问冲突而导致程序奔溃。

在这里插入图片描述
🌔针对以上情况,我们需要使用深拷贝来完成对象的拷贝构造。深拷贝:给每一个对象独立分配资源,保证多个对象之间不会因共享资源而造成多次释放导致程序崩溃问题。

写法1:

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

写法2:

// s1.swap(s2)
void swap(string& s)
{
	// 自己写了一个swap函数,库里面也有一个,区分不开编译器就会就近原则,所以要表明使用的是全局的swap
	// :: 域作用限定符 ::左边没有指定,就表明是使用全局的
	::swap(_str, s._str);
	::swap(_size, s._size);
	::swap(_capacity, s._capacity);
}

string(const string& s)
	:_str(nullptr)
	,_size(0)
	,_capacity(0)
{
	string tmp(s._str);
	swap(tmp);
}

3.赋值运算符重载

写法1:

string& operator=(const string& s)
{
	if (this != &s) // 防止自己给自己赋值
	{
		delete[] _str; 
		_str = new char[strlen(s._str) + 1];
		strcpy(_str, s._str);
	}
	return *this;
}

写法2:

🌋临时变量s在函数调用结束之后就自动销毁了,不需要自己去释放。

string& operator=(string s) //传值传参,实参拷贝构造出形参s
{
	swap(s); 
	return *this;
}

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

4.析构函数

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

string类的增删查改

🌚reserve resize 开空间

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];    // 开一个临时空间
		strncpy(tmp, _str, _size + 1);  // 将原本空间的内容按字节序拷贝给临时空间
		delete _str;                    // 清空原本的空间内容
		_str = tmp;                     // 把临时空间的内容赋值给原本空间
		_capacity = n;         
	}
}

//开空间 + 初始化
void resize(size_t n, char ch = '\0')
{
	if (n < _size)
	{
		_size = n;
		_str[_size] = '\0';
	}
	else
	{
		if (n > _capacity)
			reserve(n);

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

🌗尾插一个字符 - push_back

在这里插入图片描述

void push_back(char ch)
{
    //检查空间是否足够,若空间不够,则进行扩容
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	
	//在末尾'\0'位置插入字符,在下一个位置添置上'\0'
	_str[_size] = ch;
	_str[_size + 1] = '\0';
	
	++_size;
}

🌍追加字符串 - append

直接计算出追加过后新字符串应有的长度,查看是否需要扩容。之后把新的字符串直接拷贝在原字符串后面。

void append(const char* s)
{
    //计算追加后新字符串的长度
	size_t len = _size + strlen(s);
	
	//检查是否需要扩容
	if (len > _capacity)
		reserve(len);

	strcpy(_str + _size, s);
	_size = len;
}

🌝字符串末尾追加一个字符或者字符串 - +=

直接复用前面写的函数。

// 追加一个字符
string& operator+=(char ch)
{
	push_back(ch);
	return *this;
}

//追加一个字符串
string& operator+=(const char* str)
{
	append(str);
	return *this;
}

🌐在任意位置插入一个字符或一个字符串 - insert
1.在 i 的位置插入一个字符’x’。
在这里插入图片描述

string& insert(size_t pos, char ch)
{
    //检查传入的坐标是否合法
	assert(pos <= _size);
	//检查是否需要增容
	if (_size == _capacity)
		reserve(_capacity == 0 ? 4 : _capacity * 2);

    // 定义_size后一个位置为end
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1]; //依次挪动数据
		--end;
	}

	_str[pos] = ch;
	++_size;
	
	return *this;
}

2.在 i 的位置前面插入一个字符串“world”。在这里插入图片描述

string& insert(size_t pos, const char* str)
{
	assert(pos <= _size);
	size_t len = strlen(str);
	if (_size + len > _capacity)
		reserve(_size + len);

	//往后挪动n个位置
	char* end = _str + _size;
	while (end >= _str + pos)
	{
		*(end + len) = *end;
		--end;
	}
	strncpy(_str + pos, str, len); // 这里不能使用strcpy,因为它遇到'\0'就终止了
	_size += len;

	return *this;
}

🌑删除指定位置及大小的字符串 - erase在这里插入图片描述

// 写法1:
string& erase(size_t pos, size_t len = npos)
{
	assert(pos < _size);
	size_t Leftlen = _size - pos;

	//如果剩余的字符长度小于要删的长度(后面的全部删完)
	if (len >= Leftlen)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= pos;
	}
	return *this;
}

// 写法2:
/*string& erase(size_t pos, size_t len = npos)
{
	assert(pos < _size);
	if (len == npos || pos + len >= _size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		size_t begin = pos + len;
		while (begin <= _size)
		{
			_str[begin - len] = _str[begin];
			++begin;
		}
		_size -= len;
	}
	return *this;
}*/

🌌查找指定的字符或者字符串 - find

// 查找指定的字符
size_t find(char ch, size_t pos = 0) const
{
	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) const
{
	assert(pos < _size);
	const char* ret = strstr(_str + pos, str);
	if (ret)
		return ret - _str;
	else
		return npos;
}

🌕修改指定字符 - []
需要写两个版本,一个只读,一个可读可写。

const char& operator[](size_t i) const
{
	assert(i < _size);
	return _str[i];
}

char& operator[](size_t i)
{
	assert(i < _size);
	return _str[i];
}

string类模拟实现完整代码

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<iostream>
#include<assert.h>
#include<string.h>
using namespace std;

// 命名空间 -- 与库里面的string类进行隔离,防止冲突
namespace hyr
{
	// 管理字符串的数组,可以对字符串进行增删查改,字符串数组以'\0'结尾
	class string
	{
	public:
	
	typedef char* iterator;
	typedef const char* const_iterator;
	
	iterator begin()
	{
		return _str;
	}
	
	const iterator begin() const
	{
		return _str;
	}
	
	iterator end()
	{
		return _str + _size;
	}
	
	const iterator end() const
	{
		return _str + _size;
	}
	
	// s1.swap(s2)
	void swap(string& s)
	{
		// 自己写了一个swap函数,库里面也有一个,区分不开编译器就会就近原则,所以要表明使用的是全局的swap
		// :: 域作用限定符 ::左边没有指定,就表明是使用全局的
		::swap(_str, s._str);
		::swap(_size, s._size);
		::swap(_capacity, s._capacity);
	}
	
	//string(const char* str = "\0")    //error
	//string(const char* str = nullptr) //error
	string(const char* str = "")
	{
		// 构造string类对象,若传入了空指针,那么我们认为程序非法,我们用空字符串将其置空
		if (str == nullptr)
			str = "";
	
		_size = strlen(str);
		_capacity = _size;
		_str = new char[strlen(str) + 1]; //多开一个空间为存放'\0'
		strcpy(_str, str);
	}
	
	写法1:
	/*string(const string& s)
		:_size(strlen(s._str))
		, _capacity(_size)
	{
		_str = new char[strlen(s._str) + 1];
		strcpy(_str, s._str);
	}*/
	
	string(const string& s)
		:_str(nullptr)
		, _size(0)
		, _capacity(0)
	{
		string tmp(s._str);
		swap(tmp);
	}
	
	//string& operator=(const string& s)
	//{
	//	if (this != &s) // 防止自己给自己赋值
	//	{
	//		delete[] _str; 
	//		_str = new char[strlen(s._str) + 1];
	//		strcpy(_str, s._str);
	//	}
	//	return *this;
	//}
	
	string& operator=(string s) //传值传参,实参拷贝构造出形参s
	{
		swap(s);
		return *this;
	}
	/*string& operator=(const string& s)
	{
		if (this != &s)
		{
			string tmp(s);
			swap(tmp);
			return *this;
		}
	}*/
	
	void reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];    // 开一个临时空间
			strncpy(tmp, _str, _size + 1);  // 将原本空间的内容按字节序拷贝给临时空间
			delete _str;                    // 清空原本的空间内容
			_str = tmp;                     // 把临时空间的内容赋值给原本空间
			_capacity = n;
		}
	}
	
	//开空间 + 初始化
	void resize(size_t n, char ch = '\0')
	{
		if (n < _size)
		{
			_size = n;
			_str[_size] = '\0';
		}
		else
		{
			if (n > _capacity)
				reserve(n);
	
			for (size_t i = _size;i < n;++i)
			{
				_str[i] = ch;
			}
			_size = n;
			_str[n] = '\0';
		}
	}
	
	void push_back(char ch)
	{
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		_str[_size] = ch;
		_str[_size + 1] = '\0';
		++_size;
	}
	
	void append(const char* s)
	{
		size_t len = _size + strlen(s);
		if (len > _capacity)
			reserve(len);
	
		strcpy(_str + _size, s);
		_size = len;
	}
	
	// 追加一个字符
	string& operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	
	//追加一个字符串
	string& operator+=(const char* str)
	{
		append(str);
		return *this;
	}
	
	string& insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size == _capacity)
			reserve(_capacity == 0 ? 4 : _capacity * 2);
	
		size_t end = _size + 1;
		while (end > pos)
		{
			_str[end] = _str[end - 1];
			--end;
		}
	
		/*	char* end = _str + _size;
			while (end >= _str + pos)
			{
				*(end + 1) = *end;
				--end;
			}
			_str[pos] = ch;*/
	
		_str[pos] = ch;
		++_size;
	
		return *this;
	}
	
	string& insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);
		if (_size + len > _capacity)
			reserve(_size + len);
	
		//往后挪动n个位置
		char* end = _str + _size;
		while (end >= _str + pos)
		{
			*(end + len) = *end;
			--end;
		}
	
		/*size_t end = _size + len;
		while (end > pos + len - 1)
		{
			_str[end] = _str[end - len];
			--end;
		}*/
		strncpy(_str + pos, str, len);
		_size += len;
	
		return *this;
	}
	
	string& erase(size_t pos, size_t len = npos)
	{
		assert(pos < _size);
		size_t Leftlen = _size - pos;
	
		//如果剩余的字符长度小于要删的长度(后面的全部删完)
		if (len >= Leftlen)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			strcpy(_str + pos, _str + pos + len);
			_size -= pos;
		}
		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
		{
			size_t begin = pos + len;
			while (begin <= _size)
			{
				_str[begin - len] = _str[begin];
				++begin;
			}
			_size -= len;
		}
		return *this;
	}*/
	
	// 查找指定的字符
	size_t find(char ch, size_t pos = 0) const
	{
		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) const
	{
		assert(pos < _size);
		const char* ret = strstr(_str + pos, str);
		if (ret)
			return ret - _str;
		else
			return npos;
	}
	
	const char& operator[](size_t i) const
	{
		assert(i < _size);
		return _str[i];
	}
	
	char& operator[](size_t i)
	{
		assert(i < _size);
		return _str[i];
	}
	
	~string()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
	}
	
	size_t size() const
	{
		return _size;
	}
	
	void clear()
	{
		_size = 0;
		_str[0] = '\0';
	}
	
	const char* c_str() const
	{
		return _str;
	}
	
	private:
		char* _str;        // 储存字符串
		size_t _size;      // 字符串中有效字符个数
		size_t _capacity;  // 标识字符空间的大小
	
		static const size_t npos;
	};
	const size_t string::npos = -1;
	
	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}
	
	bool operator<(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}
	
	bool operator<=(const string& s1, const string& s2)
	{
		return s1 < s2 || s1 == s2;
	}
	
	bool operator>(const string& s1, const string& s2)
	{
		return !(s1 <= s2);
	}
	
	bool operator>=(const string& s1, const string& s2)
	{
		return !(s1 < s2);
	}
	
	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}
	
	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
			out << ch;
		return out;
	}
	
	istream& operator>>(istream& in, string& s)
	{
		//s.clear();
		//char ch;
		//ch = in.get(); //cin识别不了空格和换行
		//while (ch != ' ' && ch != '\n')
		//{
		//	s += ch;
		//	ch = in.get();
		//}
	
		//return in;
	
		char ch;
		ch = in.get();
		char buff[128] = { '\0' };
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				s += buff;
				memset(buff, '\0', 128);
				i = 0;
			}
	
			ch = in.get();
		}
	
		s += buff;
		return in;
	}
	
	istream& getline(istream& in, string& s)
	{
		s.clear();
		char ch;
		ch = in.get();
		while (ch != '\n')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}
}
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到