特殊顺序表——string模拟实现

我们之前学习了vector和list的模拟实现,今天我们学习一个特别一点的顺序表,string,string开发出来的时间比STL还要早,稍微设计的有点繁琐,我们主要实现的增删查改的功能。

文档查看

按照惯例我们先来查看文档:
在这里插入图片描述
成员函数:
在这里插入图片描述具体的可以点击相关连接:

https://legacy.cplusplus.com/reference/string/string/?kw=string

迭代器

我们先做一些基础工作:

namespace My_string
{
	class string
	{

	private:
		char* str; //字符串
		size_t size; //有效数据个数
		size_t capacity; //容量
	};
}

我们在学组件时,最重要的就是迭代器,我们来实现一下:

namespace My_string
{
	class string
	{
		typedef char* itreator; //非const迭代器
		typedef const char* const_itreator; //const迭代器

		itreator begin()
		{
			return _str;
		}

		itreator end()
		{
			return _str + _size;
		}

		const_itreator begin() const
		{
			return _str;
		}

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

	private:
		char* _str; //字符串
		size_t _size; //有效数据个数
		size_t _capacity; //容量
	};
}

构造函数

	//构造函数
		string(const char* str="")//缺省参数给空字符串默认有一个\0
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1]; 
			//开空间多开一个,存放\0
			strcpy(_str, str);
		}

测试一下:

在这里插入图片描述在这里插入图片描述

拷贝构造和赋值重载

		void Swap(string& str)
		{
			swap(_str, str._str);
			swap(_size, str._size);
			swap(_capacity, str._capacity);
		}

		//拷贝构造
		string(const string& str)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			//现代写法
			string tmp(str);
			Swap(tmp);
		}

		//赋值重载
		string& operator=(string tmp)
		{
			Swap(tmp);

			return *this;
		}

析构函数

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

c_str ( )

我们目前如果想要打印字符串,我们还无法做到,因为我们还没有重载自己的流插入和流提取,但是string有一个c_str ( ) 的接口。
在这里插入图片描述c_str ( ) 的返回值是一个指针,指向的是string的成员char* str

		//c.str()
		const char* c_str() const
		{
			return _str;
		}

现在可以尝试一下打印一下初始化过的string。

	void test1()
	{
		string s("hello world");
		cout << s.c_str() << endl;
	}

在这里插入图片描述

reserve

在这里插入图片描述
reserve改变的是容量大小,我们之后写的插入函数,要频繁进行扩容,我们先把这个函数实现一下:

		//reserve
		void reserve(size_t n = 0)
		{
			if (n > _capacity)
			{
				//开辟新空间
				char* tmp = new char[n + 1];
				//多开一个存放一个\0
				strcpy(tmp, _str);

				//删除原有空间
				delete[] _str;

				//指向新空间
				_str = tmp;

				_capacity = n;
			}
		}

push_back ( )

push_back ( )是我们的老朋友了:
在这里插入图片描述

		//push_back ( )
		string& push_back(char c)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 :
					2 * _capacity);
			}
			_str[_size++] = c;
			_str[_size] = '\0';//最后放一个\0

			return *this;
		}

测试一下:

	void test1()
	{
		string s("hello world");
		cout << s.c_str() << endl;

		s.push_back('F');
		cout << s.c_str() << endl;
	}

在这里插入图片描述

append

我们不仅可以在最后追加字符,我们还可以在最后追加字符串:
在这里插入图片描述

		//append
		string& append(const char* str)
		{
			//扩容
			size_t len = strlen(str);
			if (_size == _capacity)
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;

			return *this;
		}

测试一下:

	void test1()
	{
		string s("hello world");
		cout << s.c_str() << endl;

		s.push_back('F');
		cout << s.c_str() << endl;

		s.append("My love");
		cout << s.c_str() << endl;
	}

在这里插入图片描述

重载+=

我们发现push_back和append都是追加东西,而我们可以重载+=,实现相同的功能:
在这里插入图片描述

		string& operator+=(char c)
		{
			return push_back(c);
		}

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

		string& operator+=(const string& str)
		{
			return append(str._str);
		}

测试一下:

	void test1()
	{
		string s("hello world");
		cout << s.c_str() << endl;

		//s.push_back('F');
		s += 'F';
		cout << s.c_str() << endl;

		//s.append("My love");
		s += "My Love";
		cout << s.c_str() << endl;

		string s1("Forever");
		s += s1;
		cout << s.c_str() << endl;
	}

重载 [ ]

在这里插入图片描述

		//重载 [ ]
		char& operator[](size_t pos) //非const版本
		{
			assert(pos <= _size);
			return _str[pos];
		}

		const char& operator[](size_t pos) const //const版本
		{
			assert(pos <= _size);
			return _str[pos];
		}

测试一下:

	void test2()
	{
		string s("Forever");
		cout << s[5] << endl;
	}

在这里插入图片描述

insert

在这里插入图片描述
我们实现两个版本,一个插入字符,一个插入字符串

		//insert 字符
		string& insert(size_t pos,char c)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 *
					_capacity);
			}

			int end = _size;
			//挪动数据
			while (end >=pos)
			{
				_str[end + 1] = _str[end];
				--end;
			}

			_str[pos] = c;
			++_size;

			return *this;
		}

测试一下:

	void test2()
	{
		string s("Forever");
		s.insert(0, 'P');
		cout << s.c_str()<< endl;
	}

然后:
在这里插入图片描述

为什么呢?我们来看一下报错信息:
在这里插入图片描述

有符号与无符号不匹配?大家还记得整型提升吗?我们end是int类型,pos是size_t类型,当着两个类型相比时,int会自动提升为size_t,为无符号类型,就意味着end变为0之后,就直接变为最大整数。

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
虽然调试的时候显示的是-1,但这时候已经发生了整型提升,所以这时候的end是无符号整型,肯定比pos大。
解决办法就是pos强转为int:

		//insert 字符
		string& insert(size_t pos,char c)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 *
					_capacity);
			}

			int end = _size;
			//挪动数据
			while (end >= (int)pos)
			{
				_str[end + 1] = _str[end];
				--end;
			}

			_str[pos] = c;
			++_size;

			return *this;
		}

在这里插入图片描述

		//insert 字符串
		string& insert(size_t pos, const char* str)
		{
			assert(pos <= _size);

			size_t len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}

			int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];
				--end;
			}

			strncpy(_str + pos, str, len);
			_size += len;
			return *this;
		}
	void test2()
	{
		string s("Forever");
		s.insert(0, 'P');
		s.insert(0, "angle is bad");
		cout << s.c_str()<< endl;
	}

在这里插入图片描述

clear

在这里插入图片描述

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

erase

在这里插入图片描述
我们发现一个新的默认参数:npos,我们发现它是这样定义的:

size_t npos = -1;

这时候大家就会很疑惑-1怎么就是size_t呢?size_t是无符号整型,意思就是-1也是被当做无符号整数,而且是最大的无符号整数。上面的意思就是,如果不给定删除长度,就按npos的长度去删除。
我们去定义一下:
在这里插入图片描述
我们实现一下erase:

		//erase
		string& erase(size_t pos,size_t len = npos)
		{
			if (len == pos || pos + len > _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				int end = pos + len;
				while (end <= _size)
				{
					_str[pos++] = _str[end++];
				}

				_size -= len;
			}

			return *this;
		}

resize

在这里插入图片描述

	const string& resize(size_t n, char ch = '\0') //返回值不能被修改
		{
			if (n < _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				while (_size < n)
				{
					_str[_size++] = ch;
				}
				_str[_size] = '\0';
			}

			return *this;
		}

substr(截取子串)

在这里插入图片描述

		//截取字串
		string substr(size_t pos,size_t len = npos)
		{
			string s;
			if (len ==npos || pos + len > _size)
			{
				while (pos <= _size)
				{
					s.push_back(_str[pos++]);
				}
			}
			else
			{
				int end = pos + len; //结束位置
				while (pos < end)
				{
					s.push_back(_str[pos++]);
				}
			}
			return s;
		}

find

在这里插入图片描述
find是查找指定字符或字符串的位置。

		//查找指定字符
		size_t find(char ch, size_t pos = 0)
		{
			for (int i = pos; i < _size; i++)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}

			return npos;
		}

		//查找指定字符串
		size_t find(const char* str, size_t pos = 0)
		{
			const char* p = strstr(_str, str);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}

重载流插入和流提取( >> , << )

我们现在的流插入和流提取无法针对我们自定义类型的string,我们得重载一下流插入和流提取:

	//流输出
	ostream& operator<<(ostream& out, const string& str)
	{
		for (auto ch : str)
		{
			out << ch;
		}
		return out;
	}

测试一下:

	void test3()
	{
		string s("Forever");

		//string s1(s.erase(0, 3));
		//cout << s1.c_str() << endl;

		//s.resize(23, 'B');

		//string s2(s.substr(3, 2));
		//cout << s2.c_str() << endl;

		
		cout << s << endl;;
	}

在这里插入图片描述

	//流输入
	istream& operator>>(istream& in, string& str)
	{
		//为了减少扩容次数,首先将输入的字符存储在一个临时数组中
		//达到某一标准后,再放入str
		char buff[129];
		size_t i = 0;

		char ch;
		ch = in.get();
		while (ch != '\n')
		{
			buff[i++] = ch;
			if (i == 128)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
			ch = in.get();
		}

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

		return in;
	}

测试一下:
在这里插入图片描述

但这里出现了一个东西get是什么东西?
在这里插入图片描述
get可以提取到每一个字符,这很关键,如果我们用**>>**来输入,会捕捉不到空格和换行符。

我们来测试一下,首先我们将get换为>>

	//流输入
	istream& operator>>(istream& in, string& str)
	{
		//为了减少扩容次数,首先将输入的字符存储在一个临时数组中
		//达到某一标准后,再放入str
		char buff[129];
		size_t i = 0;

		char ch;
		//ch = in.get();
		in >> ch;
		while (ch != '\n')
		{
			buff[i++] = ch;
			if (i == 128)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
			//ch = in.get();
			in >> ch;
		}

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

		return in;
	}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
空格被忽略了!
我们再换回来:

    //流输入
	istream& operator>>(istream& in, string& str)
	{
		//为了减少扩容次数,首先将输入的字符存储在一个临时数组中
		//达到某一标准后,再放入str
		char buff[129];
		size_t i = 0;

		char ch;
		ch = in.get();
		//in >> ch;
		while (ch != '\n')
		{
			buff[i++] = ch;
			if (i == 128)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
			ch = in.get();
			//in >> ch;
		}

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

		return in;
	}

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
附上源码:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace My_string
{
	class string
	{
	public:
		typedef char* itreator; //非const迭代器
		typedef const char* const_itreator; //const迭代器

		itreator begin()
		{
			return _str;
		}

		itreator end()
		{
			return _str + _size;
		}

		const_itreator begin() const
		{
			return _str;
		}

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

		//构造函数
		string(const char* str="")//缺省参数给空字符串默认有一个\0
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1]; 
			//开空间多开一个,存放\0
			strcpy(_str, str);
		}

		void Swap(string& str)
		{
			swap(_str, str._str);
			swap(_size, str._size);
			swap(_capacity, str._capacity);
		}

		//拷贝构造
		string(const string& str)
			:_str(nullptr)
			,_size(0)
			,_capacity(0)
		{
			string tmp(str._str);
			Swap(tmp);
		}

		template<class InputItreator>
		string(InputItreator first, InputItreator end)
		{
			while (first != end)
			{
				push_back(*first);
				++first;
			}
		}

		const string& resize(size_t n, char ch = '\0') //返回值不能被修改
		{
			if (n < _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				while (_size < n)
				{
					_str[_size++] = ch;
				}
				_str[_size] = '\0';
			}

			return *this;
		}

		//截取字串
		string substr(size_t pos,size_t len = npos)
		{
			string s;
			if (len ==npos || pos + len > _size)
			{
				while (pos <= _size)
				{
					s.push_back(_str[pos++]);
				}
			}
			else
			{
				int end = pos + len;
				while (pos < end)
				{
					s.push_back(_str[pos++]);
				}
			}
			return s;
		}

		//赋值重载
		string& operator=(string tmp)
		{
			Swap(tmp);

			return *this;
		}

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

		//insert 字符
		string& insert(size_t pos,char c)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 *
					_capacity);
			}

			int end = _size;
			//挪动数据
			while (end >= (int)pos)
			{
				_str[end + 1] = _str[end];
				--end;
			}

			_str[pos] = c;
			++_size;

			return *this;
		}

		//insert 字符串
		string& insert(size_t pos, const char* str)
		{
			assert(pos <= _size);

			size_t len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}

			int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];
				--end;
			}

			strncpy(_str + pos, str, len);
			_size += len;

			return *this;
		}

		//erase
		string& erase(size_t pos,size_t len = npos)
		{
			if (len == pos || pos + len > _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				int end = pos + len;
				while (end <= _size)
				{
					_str[pos++] = _str[end++];
				}

				_size -= len;
			}

			return *this;
		}

		//查找指定字符
		size_t find(char ch, size_t pos = 0)
		{
			for (int i = pos; i < _size; i++)
			{
				if (ch == _str[i])
				{
					return i;
				}
			}

			return npos;
		}

		//查找指定字符串
		size_t find(const char* str, size_t pos = 0)
		{
			const char* p = strstr(_str, str);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}

		//重载 [ ]
		char& operator[](size_t pos) //非const版本
		{
			assert(pos <= _size);
			return _str[pos];
		}

		const char& operator[](size_t pos) const //const版本
		{
			assert(pos <= _size);
			return _str[pos];
		}

		string& operator+=(char c)
		{
			return push_back(c);
		}

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

		string& operator+=(const string& str)
		{
			return append(str._str);
		}

		//push_back ( )
		string& push_back(char c)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 :
					2 * _capacity);
			}
			_str[_size++] = c;
			_str[_size] = '\0';//最后放一个\0

			return *this;
		}

		//append
		string& append(const char* str)
		{
			//扩容
			size_t len = strlen(str);
			if (_size == _capacity)
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;

			return *this;
		}

		//reserve
		void reserve(size_t n = 0)
		{
			if (n > _capacity && _str != nullptr)
			{
				//开辟新空间
				char* tmp = new char[n + 1];
				//多开一个存放一个\0
				strcpy(tmp, _str);

				//删除原有空间
				delete[] _str;

				//指向新空间
				_str = tmp;

				_capacity = n;
			}
			else
			{
				_str = new char[n + 1];
				_size = 0;
				_capacity = n;
			}
		}

		//c.str()
		const char* c_str() const
		{
			return _str;
		}

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

	private:
		char* _str; //字符串
		size_t _size; //有效数据个数
		size_t _capacity; //容量

	public:
		const static size_t npos;
	};
	const size_t string::npos = -1;

	//流输出
	ostream& operator<<(ostream& out, const string& str)
	{
		for (auto ch : str)
		{
			out << ch;
		}
		return out;
	}

	//流输入
	istream& operator>>(istream& in, string& str)
	{
		//为了减少扩容次数,首先将输入的字符存储在一个临时数组中
		//达到某一标准后,再放入str
		char buff[129];
		size_t i = 0;

		char ch;
		ch = in.get();
		//in >> ch;
		while (ch != '\n')
		{
			buff[i++] = ch;
			if (i == 128)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
			ch = in.get();
			//in >> ch;
		}

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

		return in;
	}

	void test1()
	{
		string s("hello world");
		cout << s.c_str() << endl;

		//s.push_back('F');
		s += 'F';
		cout << s.c_str() << endl;

		//s.append("My love");
		s += "My Love";
		cout << s.c_str() << endl;

		string s1("Forever");
		s += s1;
		cout << s.c_str() << endl;
	}

	//void test2()
	//{
	//	string s("Forever");
	//	s.insert(0, 'P');
	//	s.insert(0, "angle is bad");
	//	cout << s.c_str()<< endl;
	//}

	void test3()
	{
		string s("Forever");

		//string s1(s.erase(0, 3));
		//cout << s1.c_str() << endl;

		//s.resize(23, 'B');

		//string s2(s.substr(3, 2));
		//cout << s2.c_str() << endl;

		cin >> s;
		cout << s << endl;;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值