string模拟实现

string模拟实现代码

#pragma once
#include <string>
#include <assert.h>
#include <iostream>
namespace TCH
{
	//先实现一个简单的string,只考虑从资源管理和深浅拷贝问题
	//暂且不考虑增栓查改的问题
	class string
	{
	public:
		friend bool operator< (const string& s1, const string& s2);
		friend bool operator== (const string& s1, const string& s2);
		friend bool operator<= (const string& s1, const string& s2);
			 
		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;
		}

		size_t size()const
		{
			return _size;
		}

		size_t capacity()const
		{
			return _capacity;
		}


		string& insert(size_t pos, const char ch)
		{
			//assert(pos < _size);//--------其实是可以“=”,是“=”的话就是 尾插push_back;
			assert(pos <= _size);//--------然后可以改造push_back使其“复用”insert()函数;
			if (_size == _capacity)
				reserve(_capacity == 0 ? 4 : 2 * _capacity);

			/*for (int i = _size - 1; i >= pos; i--)
			{
				_str[i + 1] = _str[i];
			}
			_str[pos] = ch;*/

			//size_t end = _size;
			size_t end = _size + 1;
			
			while (end >= pos)//-------此处头插会有大问题,因为无符号整形为-1时会变成最大整数所以end不可以给为_size
							//而是_size + 1;
			{
				_str[end] = _str[end - 1];
				end--;
			}
			_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);
			size_t end = _size + len;//-------应该是+len而不是+1;
			while(end > pos + len -1)
			//while(end - len >= pos)//怕极端场景len=0,pos = 0;
			//while (end >= pos)//-----错了!这里是挪了len个 所以条件应该也变了,多画图!
			{
				_str[end] = _str[end - len];
				end--;
			}
			size_t begin = 0;

			while (begin < len)
			{
				_str[end] = str[begin];
				end++;
				begin++;
			}
		}

		string& erase(size_t pos, size_t len)
		{
			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;
		}

		string(const char* str)//------→注意加上“const”
			:_size(strlen(str))
			, _capacity(_size)
		{
			_str = new char[_size] + 1;
			strcpy(_str, str);//------'\0'也会被拷贝进去
		}

		string(const string& s)//------一定要用上“&(引用)”
			:_size(strlen(s.c_str()))
			,_capacity(_size)

		{
			_str = new char[_size + 1];
			strcpy(_str, s.c_str());
		}
		//传统写法:老老实实干活,该开空间就开空间,该拷贝数据就自己拷贝数据
		string(const string& s)//------一定要用上“&(引用)”
			:_size(strlen(s.c_str()))
			,_capacity(_size)

		{
			_str = new char[_size + 1];
			strcpy(_str, s.c_str());
		} 

		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_capacity, s._capacity);
			std::swap(_size, s._size);
		}
		//剥削,要完成深拷贝,自己不干活,安排给别人干然后窃取劳动成果
		//现代写法--------其实也是贯彻复用的原则
		string(const string& s)
			:_str(nullptr)//-------很有必要不然下面交换后,局部对象会析构错误空间
			,_capacity(0)
			,_size(0)
		{
			string tmp(s.c_str());//--------由于这是局部对象出了作用域就会调用析构函数,所以应该先将this的_str,_capacity,_size走初始化列表初始化了才行,不然会析构错误的空间
			//std::swap(_str, tmp._str);
			//std::swap(_capacity, tmp._capacity);//--------可以直接访问私有成员,因为这是类内定义的函数,类内的成员是相通的
			//std::swap(_size, tmp._size);
			swap(tmp);

		} 
		//s1 = s3 ---------→s1.operator=(&s1,s3);

		//传统赋值写法
		string& operator= (const string& s)
		{
			if (this == &s)   //------排除自己给自己赋值的情况
				return *this;

			_size = strlen(s.c_str());
			_capacity = _size;
			if (strlen(_str) < _size)
			{
				delete[] _str;
				_str = new char[_size + 1]; 
				strcpy(_str, s.c_str());
			}
			else
			{
				strcpy(_str, s.c_str());
			}
			

			return *this;
		}
		//现代赋值写法一:
		string& operator= (const string& s)
		{
			string tmp(s.c_str());
			swap(tmp);
			return *this;
		}
		//现代赋值写法二:
		string& operator= (string s)
		{
			swap(s);
			return *this;
		}

		//s1 = s3 ---------→s1.operator=(&s1,s3);
		string& operator= (const string& s)
		{
			if (this == &s)   //------排除自己给自己赋值的情况
				return *this;

			_size = strlen(s.c_str());
			_capacity = _size;
			if (strlen(_str) < _size)
			{
				delete[] _str;
				_str = new char[_size + 1]; 
				strcpy(_str, s.c_str());
			}
			else
			{
				strcpy(_str, s.c_str());
			}

			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];
		}

		void resize(size_t n, char ch) 
		{
			if (n > _capacity)//-------分情况:大于_capacity
			{
				reserve(n);
				for (int i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';//-------一定不要忘记给‘\0’;
				_size = n;
			}
			else if (n > _size)//-------分情况:小于_capacity但大于_size
			{
				for (int i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
			}

			else//------分情况:小于size
			{
				_size = n;
				_str[n] = '\0';
			}
				 
		}

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];//-------一定不要忘了多开一个空间给‘\0’用
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		void push_back(const char ch)
		{
			/*if (_size == _capacity)
			{
				//char* tmp = new char[_capaci ty * 2 + 1];//----每次都要多开出一个空间给‘\0’;
				//strcpy(tmp, _str);//--------旧空间拷贝给新空间!
				_capacity = _capacity * 2 + 1;-------❌:根本不需要+1,自己好好想想,实际空间就是能给存储的有效字符不能包括‘、0’
				//_capacity *= 2;
				//delete[] _str;
				//_str = tmp;
				reserve(_capacity == 0 ? 4 : 2 * _capacity);//-------要懂得“复用”
						//有bug:当一开始为空串的时候,capacity=0,则应该先给具体的数字
						//否则会一直传0
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';//---------→‘\0’不能忘记处理!*/


			insert(_size, ch);//--------“复用”insert函数

		}

		string& operator+= (const char ch)
		{
			push_back(ch);//-------复用直接不需要自己重新写了
			return *this;
		}

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

		string& append(const char* str)
		{
			size_t new_size=strlen(str) + _size;
			if (new_size > _capacity)
			{
				/*char* tmp = new char[new_size * 2 + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_capacity = new_size * 2; */

				reserve(new_size);
			}
			//for()-----不需要一个一个尾插,还是用strcpy就行了
			strcpy(_str + _size, str);//---------记住是  _str+_size的位置开始拷贝;
			_size = new_size;
		}

		~string()
		{
			if (_str)
				delete[] _str;
		}
		const char* c_str()const
		{
			return _str;
		}

		char& operator[] (size_t pos)
		{
			assert(pos < strlen(_str));
			return _str[pos];
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;
		const static size_t npos;
	};
	const size_t string:: npos = -1;//--------静态成员变量定义
	bool operator< (const string& s1, const string& s2)
	{
		return std::strcmp(s1.c_str(), s2.c_str()) < 0;//---------不要老是忘了c_str()是函数不能少了()知道吗!
	}

	bool operator== (const string& s1, const string& s2)
	{
		return std::strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<= (const string& s1, const string& s2)
	{
		return (s1 < s2 || s1 == s2);//--------记得“复用”;
	}

	std::ostream& operator<< (std::ostream& out, const string& s)
	{
		for (auto ch : s)
			//out << s.c_str() << std::endl;//-------流插入和流提取不一定要友元函数,要看是否访问了私有成员
											//此写法不算对,因为当string里面有’\0‘的时候就会打错!
		{
			out << ch;//--------这样才能保证每一个都能打印出来
		}
		return out;
	}

	std::istream& operator>> (std::istream& in, string& s)
	{
		char ch;
		char buff[128] = { '\0' };//----全部初始化为'\0';
		size_t i = 0;
		ch = in.get();
		while (ch != ' ' && ch != '/n')
		{
			buff[i++] = ch;
			if (i == 127)//-----说明buff有效字符已满
			{
				s += buff;
				memset(buff, 0, sizeof(buff));
			}
			ch = in.get();
			/*in >> ch;
			s += ch;*/
		}
		s += buff;
		return in;
	}

}

string模拟实现的问题和细节

1.

string(const char* str)//------→注意加上“const”
			:_size(strlen(str))
			, _capacity(_size)
			, _str(new char[_size])------❌:走初始化列表的话定义的顺序是按声明的顺序来的,所以会先执行_str(.....),
所以可以将_str(.....)放在{}大括号里面定义便可以避免;								

private:
		char* _str;------声明的顺序 
		size_t _size;
		size_t _capacity;

2.

string s1;---------➡️库里面的无参的构造
string(const char* str = “”)-------在有参的构造函数下给一个全缺省“空串													 空串默认是个’\0';-------➡️“\0”(也可以),但是‘\0’是不可以的,他会转化成Acciall码值0,是个空指针;
			:_size(strlen(str))
			, _capacity(_size)
			{
				_str = new char[_size] + 1;
				strcpy(_str, str);//------'\0'也会被拷贝进去
			}

3.string的迭代器(原生指针)

typedef char* iterator;
iterator begin()
{
	return _str;
}

iterator end()
{
	return _str + _size;
}

string的迭代器是原生指针,不需要重载++

4.

 iterator begin()const--------❌:这样写是错误的,这样能够支持写(修改)!
 								因为你返回的是普通迭代器,与自己是const不相吻合
{
	return _str;
}

const iterator begin()const-----✅:返回const的对象则是符合的!
{
	return _str;
}

5.✳️值得搞清楚!insert()头插问题还是比较典型

size_t end = _size;--------✳️只要对此处做出变动就行
while (end >= pos)//-------此处头插会有大问题,因为无符号整形为-1时会变成最大整数(npo  s)
{
	_str[end + 1] = _str[end];
	end--;
}
	_str[pos] = ch;
	_size++;

解决方案:我们将end规定为\0的后面,则当pos和end都指向第一个位置的时候就停止,则end就不会减到-1转为最大整形数字了请添加图片描述

size_t end = _size + 1;--------✳️只要对此处做出变动就行
while (end >= pos)
{
	_str[end] = _str[end -1 ];
	end--;
}
	_str[pos] = ch;
	_size++;


移动多个字符
while(end > pos + len -1)
//while(end - len >= pos)//怕极端场景len=0,pos = 0;
//while (end >= pos)//-----错了!这里是挪了len个 所以条件应该也变了,多画图!
{
	_str[end] = _str[end - len];
	end--;
}

6.

流插入和流提取不一定要友元!只有访问了私有成员才需要友元!

std::ostream& operator<< (std::ostream& out, const string s)
	{
		for (auto ch : s)
	//out << s.c_str() << std::endl;//-------流插入和流提取不一定要友元函数,要看是否访问了私有成员
									//此写法不算对,因为当string里面有’\0‘的时候就会打错!
		{
			out << ch;//--------这样才能保证每一个都能打印出来
		}
		return out;
	}

7.流提取遇到问题


cin >> ch1 >> ch2;
能够得出cin认为空格和换行是多个值的间隔,则ch永远都不能拿到空格和换行符;

while (ch != ' ' && ch != '/n')----❌:因为ch永远拿不到空格和换行符
{
	in >> ch;
	s += ch;
}


需要用in.get()函数,能够到缓冲区一个个都取字符
char ch;
ch = in.get();
while (ch != ' ' && ch != '/n')
{
	s += ch;
	ch = in.get();
}


提高效率的写法,不用频繁扩空间
ch = in.get();
while (ch != ' ' && ch != '/n')
{
	buff[i++] = ch;
	if (i == 127)//-----说明buff有效字符已满
	{
		s += buff;
		memset(buff, 0, sizeof(buff));
	}
	ch = in.get();
}
s += buff;

8.实现getline()就是上面改造而来

ch = in.get();
while (ch != '/n')----就是吧空格条件抹掉
{
	buff[i++] = ch;
	if (i == 127)//-----说明buff有效字符已满
	{
		s += buff;
		memset(buff, 0, sizeof(buff));
	}
	ch = in.get();
}
s += buff;

9.现代和传统写法

//传统写法:老老实实干活,该开空间就开空间,该拷贝数据就自己拷贝数据
string(const string& s)//------一定要用上“&(引用)”
	:_size(strlen(s.c_str()))
	,_capacity(_size)

{
	_str = new char[_size + 1];
	strcpy(_str, s.c_str());
} 
  
void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_capacity, s._capacity);
	std::swap(_size, s._size);
}
		//剥削,要完成深拷贝,自己不干活,安排给别人干然后窃取劳动成果
		//现代写法--------其实也是贯彻复用的原则
string(const string& s)
	:_str(nullptr)//-------很有必要不然下面交换后,局部对象会析构错误空间
	,_capacity(0)
	,_size(0)
{
	string tmp(s.c_str());//--------由于这是局部对象出了作用域就会调用析构函数,所以应该先将this的_str,_capacity,_size走初始化列表初始化了才行,不然会析构错误的空间
	//std::swap(_str, tmp._str);
	//std::swap(_capacity, tmp._capacity);//--------可以直接访问私有成员,因为这是类内定义的函数,类内的成员是相通的
	//std::swap(_size, tmp._size);
	swap(tmp);

} 


//传统赋值写法
string& operator= (const string& s)
{
	if (this == &s)   //------排除自己给自己赋值的情况
	return *this;

	_size = strlen(s.c_str());
	_capacity = _size;
	if (strlen(_str) < _size)
	{
		delete[] _str;
		_str = new char[_size + 1]; 
		strcpy(_str, s.c_str());
	}
	else
	{
		strcpy(_str, s.c_str());
	}
			

	return *this;
}
//现代赋值写法一:
string& operator= (const string& s)
{
	string tmp(s.c_str());
	swap(tmp);
	return *this;
}
//现代赋值写法二:
string& operator= (string s)
{
	swap(s);
	return *this;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值