2024年C++_String增删查改模拟实现(1),2024年最新凭借这份《数据结构与算法》核心文档

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

下面给出各位初学者容易犯的错误:

void insert(size_t pos, char ch)
{
	assert(pos <= _size);
	//扩容
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity \* 2);
	}
	//挪动数据
	size_t end = _size;
	while (end >= pos)
	{
		_str[end+1] = _str[end];
		end--;
	}
	_str[pos] = ch;
	_size++
}

这样对吗?答案是错误的。

假设是在头插字符,end理论上和pos(即0)比较完后就减到-1,在下一次循环条件比较时失败,退出循环。
遗憾的是end是size_t类型,始终>=0, 会导致死循环。

博主在这给出两种解决方法:

  1. 将pos强转为整型。
void insert(size_t pos, char ch)
{
	assert(pos <= _size);
	//扩容
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity \* 2);
	}
	//挪动数据
	int end = _size;
	while (end >= (int)pos)
	{
		_str[end+1] = _str[end];
		end--;
	}
	_str[pos] = ch;
	_size++
}

2.从end从最后数据的后一位开始,每次将前一个数据移到当前位置。最后条件判断就转化为end>pos,不会出现死循环这种情况。
在这里插入图片描述

void 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--;
	}
	//插入数据,更新\_size
	_str[pos] = ch;
	_size++;
}

5.4.2 insert插入字符串

insert同样存在相同问题,并且思路一样。博主就直接给出代码了。
法一:

void insert(size_t pos, const char\* str)
{
	int len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}

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

法二:

void insert(size_t pos, const char\* str)
{
	int len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}
	
	size_t end = _size+1;
	while (end > pos)
	{
		_str[end + len-1] = _str[end-1];
		end--;
	}
	strncpy(_str + pos, str, len);
	_size += len;
}

5.5 erase

erase分两种情况:

  1. 从pos开始,要删的数据个数超过的字符串,即将pos后序所有数据全部情况。(直接将pos处数据置为’\0’即可)
  2. 从pos开始,要删的数据个数没有超出的字符串。所以只需要从pos+len位置后的所有数据向前移动从pos位置覆盖原数据即可。
void erase(size_t pos, size_t len = npos)
{
	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;
	}
}

六、 关系操作符重载:< 、 ==、 <=、 >、>=、!=

bool operator<(const string& s) const
{
	return strcmp(_str, s._str) < 0;
}

bool operator==(const string& s) const
{
	return strcmp(_str, s._str) == 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);
}

七、find查找字符、字符串、substr

7.1 find查找字符

size_t find(char ch, size_t pos = 0)
{
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}

	return npos;
}

7.2 find查找字符串

size_t find(const char\* sub, size_t pos = 0)
{
	const char\* p = strstr(_str + pos, sub);
	if (p)
	{
		return p - _str;
	}
	else
	{
		return npos;
	}
}

7.3 strsub( ) 模拟实现

strsub目标长度可能越界string,也可能还有没有。但不管是那种情况,最后都需要拷贝数据。所以这里我们可以先将len真实长度计算出来,在拷贝数据。

string substr(size_t pos, size_t len = npos)const
{
	string s;
	size_t end = pos + len;
	//目标字符越界string,更新len
	if (len == npos || end >= _size)
	{
		len = _size - pos;
		end = _size;
	}
	
	//拷贝数据
	s.reserve(len);
	for (size_t i = pos; i < end; i++)
	{
		s += _str[i];
	}

	return s;
}

八、流插入和流提取(<<、>>)(实现在string类外)

8.1 流插入<<

由于前面我们实现了迭代器,所以最简单的方式就是范围for

ostream& operator<<(ostream& out, const string& s)
{
	/\*for (size\_t i = 0; i < s.size(); i++)
 {
 out << s[i];
 }\*/
	for (auto ch : s)
		out << ch;

	return out;
}

8.1 流提取>>

流提取比较特殊。在流提取前需要将原有数据全部清空。同时由于>>无法获取空字符和换行符()(都是作为多个值之间的间隔),直接流提取到ostream对象中,没法结束。(类似于C语言中scanf, 换行符和空字符仅仅只是起到判断结束的作用,但scanf无法获取到它们)
所以这里博主直接调用istream对象中的get()函数。(类似于C语言中的getchar()函数)
get详细文档
在这里插入图片描述

class string
{
	void clear()
	{
		_str[0] = '\0';
		_size = 0;
	}
private:
	char\* _str;
	size_t _capacity;
	size_t _size;
};

istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char ch;
		//in >> ch;
		ch = in.get();

		while (ch != ' ' && ch != '\n')
		{
			s += ch;
			//in >> ch;
			ch = in.get();
		}
		return in;
	}

上面这种方法虽然可以达到目的。但还有一个问题,每次插入数据都面临可扩容问题。那如何优化呢?

优化

其中一种办法就是调用reserve()提前开好空间,但这样面临这另一个问题:开大了浪费空间;开小了,同样面临这扩容的问题。
所以在这博主采用和vs底层实现的思路:首先开好一段数组(包含’\0’,以16为例)。当数据个数小于16时,字符串存在数组中;当数据个数大于等于16时,将数据存在_str指向的空间。
这是一种以空间换时间的思路,同时也能很好的减少内存碎片的问题。

class string
{
	void clear()
	{
		_str[0] = '\0';
		_size = 0;
	}
private:
	char\* _str;
	size_t _capacity;
	size_t _size;
};

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

	char buff[16];
	size_t i = 0;

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

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

九、所有代码

namespace achieveString
{
	class string
	{
	public:
	typedef char\* iterator;
	const typedef 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()
 :\_str(new char[1]{'\0'})
 ,\_capacity(0)
 ,\_size(0)
 { }\*/
		string(const char\* str = "")
			:\_capacity(strlen(str))
			, \_size(_capacity)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		const char\* c\_str() const
		{
			return _str;
		}

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

		//拷贝构造
		/\*string(const string& s)
 {
 \_str = new char[s.\_capacity + 1];
 strcpy(\_str, s.\_str);
 \_size = s.\_size;
 \_capacity = s.\_capacity;
 }\*/

		//交换
		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		//现代写法
		string(const string& s)
			:\_str(nullptr)
			,\_size(0)
			,\_capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
		}

		// 赋值重载
		/\*string& operator=(const string& s)
 {
 if (this != &s)
 {
 char\* tmp = new char[s.\_capacity + 1];
 strcpy(tmp, s.\_str);
 delete[] \_str;
 \_str = tmp;
 \_size = s.\_size;
 \_capacity = s.\_capacity;
 }
 return \*this;
 }\*/
		//现代写法
		//法一
		/\*string& operator=(const string& s)
 {
 if (this != &s)
 {
 string tmp(s.\_str);
 swap(tmp);
 }
 return \*this;
 }\*/
		//法二
		string& operator=(string tmp)
		{
			swap(tmp);
			return \*this;
		}

		//可读可写
		char operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		//只读
		const char operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}

		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _capacity;
		}

		bool empty()const
		{
			return _size == 0;
		}

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char\* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}

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

		void push\_back(char ch)
		{
			//扩容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity \* 2);
			}
			//插入数据
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
		
		}

		void append(const char\* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
		}

		string& operator+=(char ch)
		{
			push\_back(ch);
			return \*this;
		}

		string& operator+=(const char\* str)
		{
			append(str);
			return \*this;
		}
		
		void 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--;
			}
			//插入数据,更新\_size
			_str[pos] = ch;
			_size++;
		}
		void insert(size_t pos, const char\* str)
		{
			int len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			//法一
			/\*int end = \_size;
 while (end >= (int)pos)
 {
 \_str[end + len] = \_str[end];
 end--;
 }
 strncpy(\_str + pos, str, len);
 \_size += len;\*/

			//法二
			size_t end = _size+1;
			while (end > pos)
			{
				_str[end + len-1] = _str[end-1];
				end--;
			}
			strncpy(_str + pos, str, len);
			_size += len;
		}

		void erase(size_t pos, size_t len = npos)
		{
			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;
			}
		}
		bool operator<(const string& s)const
		{
			return strcmp(_str, s._str) < 0;
		}

		bool operator==(const string& s)const
		{
			return strcmp(_str, s._str) == 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);
		}

		size_t find(char ch, size_t pos = 0)
		{
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
					return i;
			}
			return npos;
		}

		size_t find(const char\* sub, size_t pos = 0)
		{
			const char\* p = strstr(_str + pos, sub);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos, size_t len = npos)
		{
			string s;
			size_t end = pos + len;
			if (len == npos || end >= _size)
			{
				len = _size - pos;
				end = _size;
			}
			
			s.reserve(len);
			for (size_t i = pos; i < end; i++)
			{
				s += _str[i];
			}

			return s;
		}


![img](https://img-blog.csdnimg.cn/img_convert/42bf5fec97925f33f0a260a8d6043980.png)
![img](https://img-blog.csdnimg.cn/img_convert/afdc7fc6d35565dd4de3cafdfe15a5ba.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

ze_t pos = 0)
		{
			const char\* p = strstr(_str + pos, sub);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos, size_t len = npos)
		{
			string s;
			size_t end = pos + len;
			if (len == npos || end >= _size)
			{
				len = _size - pos;
				end = _size;
			}
			
			s.reserve(len);
			for (size_t i = pos; i < end; i++)
			{
				s += _str[i];
			}

			return s;
		}


[外链图片转存中...(img-PEDGwRZ1-1715568572424)]
[外链图片转存中...(img-MDMkM8AC-1715568572424)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值