string的模拟实现

拷贝构造和赋值重载的传统写法vs现代写法

传统写法

//传统写法

		//拷贝构造
		string(const string& s1)
		{
			assert(s1._str);
			
			_size = _capacity = strlen(s1._str);
			_str = new char[_capacity + 1];
			strcpy(_str, s1._str);

		}

		
		//赋值重载
		string& operator= (const string& s1)
		{
			//深拷贝
			delete[]_str;
			
			_size = _capacity = strlen(s1._str);
			_str = new char[_capacity + 1];
			strcpy(_str, s1._str);

			return *this;
		}

现代写法:

//现代写法

		//拷贝构造
		string(const string& s1)
			:_str(nullptr)
		{
			assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝

			string tmp(s1._str);
			std::swap(_str, tmp._str);
			_size = _capacity = strlen(_str);

		}


		//赋值重载
		string& operator= (const string& s1)
		{
			string tmp(s1);
			std::swap(_str, tmp._str);
			_capacity = _size = strlen(_str);


			return *this;
		}

拷贝构造和赋值重载的交换

//实现直接交换类的swap,库里的swap也能完成,但要完成三次深拷贝,效率低下
		//系统提供的:
		/*	template <class T> void swap(T&a, T&b)
			{
				//string的拷贝,三次深拷贝
				T c(a);
				a = b;
				b = c;
			}
		*/

		//自己实现的
		void swap(string& s)
		{
			//swap使用后,在局部作用域会默认只使用一种参数类型的交换,所以要用::来表明它是全局的,不受局部限制
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}

拷贝构造和赋值重载的现代写法可以复用swap

//现代写法

		//拷贝构造
		string(const string& s1)
			:_str(nullptr)
		{
			assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝

			string tmp(s1._str);
			swap(tmp);

		}


		//赋值重载
		string& operator= (const string& s1)
		{
			string tmp(s1);
			swap(tmp);

			return *this;
		}

范围for

如果我们把迭代器的名称一改,由begin->Begin,end->End,范围for就实现不了了

image-20211015111027931

所以说范围for的底层是由迭代器实现的

“增”的操作(从尾部添加)

字符:push_back

字符串:append

还有一个很好用的operator+=,可以复用上面两个函数

既然要添加字符,就不得不涉及到增容:reserve

reserve实现

	//reserve
		void reserve(size_t capacity)
		{
			if (capacity > _capacity)
			{
				char* tmp = new char[capacity + 1];
				strncpy(tmp, _str, _size + 1);//要用strncpy或memcpy拷贝,如果用strcpy,可能源字符串是"hello\0\0\0\0",这样就会导致拷贝的内容不一致

				delete[] _str;
				_str = tmp;

				_capacity = capacity;

			}
			
		}

既然有reserve,那么也得提到resize

resize的实现:

//resize
		 void resize(int n, char x = '\0')
		{ 
			 //有三种情况:
			 //1.n <= _size,将n赋值给_size,_str[_size] = '\0'
			 //2._size < n <= _capacity,循环将多余的位置设置为'\0'
			 //3.n > _capacity,先reserve(n),再将多余的位置设置为'\0'

			 if (n <= _size)
			 {
				 _size = n;
				 _str[_size] = '\0';

			 }
			 else if (n <= _capacity)
			 {
				 int end = n;
				 while (end > _size)
				 {
					 _str[end] = '\0';
					 end--;
				 }

				 _size = n;
			 }
			 else
			 {
				 reserve(n);

				 int end = n;
				 while (end > _size)
				 {
					 _str[end] = '\0';
					 --end;
				 }

				 _size = n;
			 }
		}

“改”的实现:insert

特殊情况:pos和是size_t的类型且pos为0时

<<、>>及inline的重载

<<容易实现,>>的输入遇到空格和回车时会停止读取,所以采用循环读取字符的方式,当字符为空格或回车时,就停止输入,可以用cin.get()接口来实现字符的读取,类似getchar

getline的原理与>>类似,遇到回车才停止输入。

注意:>>和getline的输入是覆盖输入的,所以输入前需要用clear接口清空

总体实现:

#pragma once

#include<iostream>
#include<string.h>
#include<assert.h>

using namespace std;

namespace ysj{
	

	class string {
		
	public:
		typedef char* iterator;

		//*****************************************************************************************************************************
		//构造函数
		string(const char* str = "")//默认缺省值不给nullptr,因为下面要将str作为strlen的参数,会引发错误
		{
			_size = _capacity = strlen(str);//_capacity是指有效容量,不包括\0,但实际开的空间要多一个字节容纳\0
			_str = new char[_capacity + 1];
			strcpy(_str, str);

		}


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

		//传统写法

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

		}*/

		
		//赋值重载
		//string& operator= (const string& s1)
		//{
		//	//深拷贝
		//	delete[]_str;
		//	
		//	_size = _capacity = strlen(s1._str);
		//	_str = new char[_capacity + 1];
		//	strcpy(_str, s1._str);

		//	return *this;
		//}


		//现代写法

		//拷贝构造
		string(const string& s1)
			:_str(nullptr)
		{
			assert(s1._str);//不能直接写成s1,会陷入无限递归的拷贝

			//string tmp(s1._str);
			//std::swap(_str, tmp._str);
			//_size = _capacity = strlen(_str);

			string tmp(s1._str);
			swap(tmp);

		}


		//赋值重载
		string& operator= (const string& s1)
		{
			/*string tmp(s1);
			std::swap(_str, tmp._str);
			_capacity = _size = strlen(_str);*/

			string tmp(s1);
			swap(tmp);

			return *this;
		}

		//实现直接交换类的swap,库里的swap也能完成,但要完成三次深拷贝,效率低下
		/*	template <class T> void swap(T&a, T&b)
			{
				//string的拷贝,三次深拷贝
				T c(a);
				a = b;
				b = c;
			}
		*/

		
		void swap(string& s)
		{
			//swap使用后,在局部作用域会默认只使用一种参数类型的交换,所以要用::来表明它是全局的,不受局部限制
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}

		//*********************************************************************************************************************


		//*********************************************************************************************************************

		//查

		//1.[]重载
		//可读可写返回
		char& operator[](size_t pos)
		{
			assert(pos <= size());

			return _str[pos];
		}

		//只读返回
		char& operator[](size_t pos)const
		{
			assert(pos <= size());

			return _str[pos];
		}


		//2.迭代器
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		//const迭代器
		const iterator begin()const
		{
			return _str;

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


		//返回string的有效字符数字
		size_t size()
		{
			return _size;
		}

		//3.范围for,其本质就是用迭代器实现的

		//万一operatoe[]想以只读的方式返回,size()也要是const的
		size_t size()const
		{
			return _size;
		}
		//*********************************************************************************************************************


		//*********************************************************************************************************************


		//增
		//1.push_back
		void push_back(char x)
		{
			//判断增容,若需要增容,使用reserve增容
			if (_size == _capacity)
			{
				int capacity = (_capacity == 0 ? 4 : 2 * _capacity);
				reserve(capacity);
			}

			_str[_size] = x;
			_str[_size + 1] = '\0';
			++_size;

		}


		//2.append
		void append(const char* str)
		{
			int len = strlen(str) + _size;

			if (len > _capacity)
			{
				reserve(len);
			}

			strncpy(_str + _size, str, strlen(str) + 1);
			_size = len;
			
			_str[_size] = '\0';
		}

		//3.operator+=
		//可复用append和push_back的代码
		
		//字符
		string& operator+=(char x)
		{
			push_back(x);

			return *this;
		}

		//字符串
		string& operator+=(const char* str)
		{
			append(str);

			return *this;
		}

		//reserve
		void reserve(size_t capacity)
		{
			if (capacity > _capacity)
			{
				char* tmp = new char[capacity + 1];
				strncpy(tmp, _str, _size + 1);//要用strncpy或memcpy拷贝,如果用strcpy,可能源字符串是"hello\0\0\0\0",这样就会导致拷贝的内容不一致

				delete[] _str;
				_str = tmp;

				_capacity = capacity;

			}
			
		}

		//resize
		 void resize(int n, char x = '\0')
		{ 
			 //有三种情况:
			 //1.n <= _size,将n赋值给_size,_str[_size] = '\0'
			 //2._size < n <= _capacity,循环将多余的位置设置为'\0'
			 //3.n > _capacity,先reserve(n),再将多余的位置设置为'\0'

			 if (n <= _size)
			 {
				 _size = n;
				 _str[_size] = '\0';

			 }
			 else if (n <= _capacity)
			 {
				 int end = n;
				 while (end > _size)
				 {
					 _str[end] = '\0';
					 end--;
				 }

				 _size = n;
			 }
			 else
			 {
				 reserve(n);

				 int end = n;
				 while (end > _size)
				 {
					 _str[end] = '\0';
					 --end;
				 }

				 _size = n;
			 }
		}


		//*********************************************************************************************************************



		//*********************************************************************************************************************
		 //改,insert,在pos之前插入
		 
		 //字符
		 void insert(char x, size_t pos)
		 {
			 assert(pos <= _size);

			 if (_size == _capacity)
			 {
				 reserve(2 * _capacity);
			 }

			 size_t end = _size + 1;//如end等于_size+1,当pos等于0时,下面的while循环中,end永远不会小于0,就陷入了死循环

			 while (end > pos)
			 {
				 _str[end] = _str[end - 1];
				 --end;
			 }
			 _str[pos] = x;

			 ++_size;
		 }

		 //字符串
		 void insert(const char* s, size_t pos)
		 {
			 assert(pos <= _size);

			 int len = strlen(s);

			 if (len + _size > _capacity)
			 {
				 reserve(len + _size);
			 }

			 size_t end = _size + 1;//如end等于_size+1,当pos等于0时,下面的while循环中,end永远不会小于0,就陷入了死循环

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

			 strncpy(_str + pos, s, strlen(s));

			 _size += len;
		 }



		//*********************************************************************************************************************



		//*********************************************************************************************************************
		 //删,erase,从pos位置开始删除
		 ///默认从开头删
		 string& erase(size_t pos = 0, size_t len = npos)//静态缺省变量npos,表示当没有给出删除的位置时,默认从npos开始,也就是字符串的末尾
		 {
			 assert(pos < _size);

			 if (len > _size - pos)//剩余字符长度小于删除字符长度
			 {
				 //将从pos位置开始的之后所有字符都删除
				 _str[pos] = '\0';
				 _size = pos;
			 }
			 else//向前挪
			 {
				 strncpy(_str + pos, _str + pos + len, strlen(_str + pos + len) + 1);
				 _size -= len;
			 }

			 return *this;

		 }


		//*********************************************************************************************************************
		



		//*********************************************************************************************************************
		
		 //find、rfind的实现
		 //find

		 //字符
		 size_t find(char x, size_t pos = 0)//找到了返回下标,没找到返回npos
		 {
			 for (int i = pos; i < _size; ++i)
			 {
				 if (x == _str[i])
					 return i;
			 }

			 return npos;
		 }

		 //字符串
		 size_t find(const char*s, size_t pos = 0)
		 {
				char* ret = nullptr;
			 //用strstr查找
			 for (int i = pos; i < _size; ++i)
			 {
				 ret = strstr(_str + i, s);

				 if (ret)
				 {
					 return ret - _str;
				 }
			 }

			 return npos;
		 }

		 //refind

		 //字符
		 size_t rfind(char x,  size_t pos = npos)//默认从最后开始查找
		 {

			 if (pos >= _size)
			 {
				 pos = _size - 1;
				 

			 }

			 for (int i = pos; i >= 0; --i)
			 {
				 if (_str[i] == x)
				 {
					 return i;
				 }
			 }

			 return npos;


		 }

		 //字符串
		 size_t rfind(const char* s, size_t pos = npos)
		 {
			 if (pos >= _size)
			 {
				 pos = _size - 1;
			 }

			 char* ret = nullptr;
			 for (int i = pos; i >= 0; --i)
			 {
				 ret = strstr(_str + i, s);

				 if (ret)
				 {
					 return ret - _str;
				 }
			 }

			 return npos;
		 }



		 //*********************************************************************************************************************

		 //clear重载,清空字符串
		 void clear()
		 {
			 _size = 0;
			 _str[0] = '\0';
		 }


		//*********************************************************************************************************************

		  //>的重载
		 bool operator>(const string& s)const
		 {
			 assert(s._str);
			 return strcmp(_str, s._str) > 0;
		 }



		 //==的重载
		 bool operator==(const string& s)const
		 {
			 assert(s._str);
			 return strcmp(_str, s._str) == 0;

		 }

		 //<的重载
		 bool operator<(const string& s)const
		 {
			 assert(s._str);
			 return (!(*this > s)) && (!(*this == s));
		 }

		 //>=的重载
		 bool operator<=(const string& s)const
		 {
			 assert(s._str);
			 return (*this < s) || (*this == s);
		 }
		 //<=的重载
		 bool operator>=(const string& s)const
		 {
			 assert(s._str);
			 return (*this > s) || (*this == s);

		 }
		 //!=的重载
		 bool operator!=(const string& s)const
		 {
			 assert(s._str);
			 return !(*this == s);


		 }

		 //*********************************************************************************************************************

	


		//*********************************************************************************************************************

		void Print()
		{
			for (int i  = 0; i < size(); ++i)
				cout << _str[i];
			cout << endl;
		}
		

	private:
		char* _str;
		int _size;
		int _capacity;
		static const size_t npos;
	};

	const size_t string::npos = -1;

	//<< >>的重载

	//<<的重载
	ostream& operator<<(ostream& out, const string& s)
	{
		for (int i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}

		return out;
	}

	//>>的重载
	istream& operator>>(istream& in, string& s)
	{
		s.clear();

		char ch = in.get();//类似getchar,将字符串输入缓冲区,再一个字符一个字符读取

		while (ch != '\n' && ch != ' ')//只要不是回车或空格,就继续输入
		{
			s += ch;
			ch = in.get();
		}

		return in;
	}

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

		char ch = in.get();//类似getchar,将字符串输入缓冲区,再一个字符一个字符读取

		while (ch != '\n')//只要不是回车或空格,就继续输入
		{
			s += ch;
			ch = in.get();
		}

		return in;
		
	}

	//测试

	//默认成员函数的测试
	void TestString1()
	{
		string s1;
		string s2("hello world");
		string s3(s2);
		string s4;
		

		s1.Print();
		s2.Print();
		s3.Print();

		s1 = s3;
		s1.Print();

		s4.swap(s3);
		s4.Print();

	}

	//查找的测试,operator[],迭代器,范围for
	void TestString2()
	{
		string s1("hello world");
		
		for (size_t i = 0; i <= s1.size(); ++i)
		{
			cout << s1[i];
		}
		cout << endl;

		string::iterator begin = s1.begin();
		for (; begin != s1.end(); ++begin)
		{
			cout << *begin;
		}
		cout << endl;

		for (auto e : s1)
		{
			cout << e;
		}
		cout << endl;
	}

	//增的测试,push_back,append,operator+=,reserve,resize
	void TestString3()
	{
		string s1("hello world");
		string s2 = ("hello string");
		string s3 = ("hello ");
		s1.push_back('a');
		s2.append("tttt");
		s3 += "world";

		s1.Print();
		s2.Print();
		s3.Print();

		s1.resize(5);
		s1 += "#####";
		s2.resize(20);
		s2 += "####";
		s3.resize(20);
		s3 += "####";

		s1.Print();
		s2.Print();
		s3.Print();
	}

	//改的测试,insert
	void TestString4()
	{
		string s1("hello");
		string s2(s1);
		s2.insert('a', 0);
		s2.Print();
		s2.insert('a', 3);
		s2.Print();
		s2.insert('a', s2.size());
		s2.Print();

		s1.insert("abc", 0);
		s1.Print();

		s1.insert("abc", 3);
		s1.Print();

		s1.insert("abc", s1.size());

		s1.Print();

	}
	
	//删的测试,erase
	void TestString5()
	{
		string s1("hello world");
		string s2(s1);
		string s3(s1);
		s1.erase();
		s2.erase(0, 2);
		s3.erase(3);

		s1.Print();
		s2.Print();
		s3.Print();
	}

	//find,refind的测试
	void TestString6()
	{
		string s1("http://www.cplusplus.com/");
		cout << s1.find('a') <<endl;
		cout << s1.find("plus") << endl;

		string s2("http://www.cplusplus.com/");
		cout << s2.rfind('a') << endl;
		cout << s2.rfind("plus") << endl;


	}

	//< > == >= <= !=重载测试
	void TestString7()
	{
		string s1("hello");
		string s2("hello");

		string s3("hellp");

		cout << (s1 == s2) << endl;
		cout << (s1 > s2) << endl;
		cout << (s1 < s2) << endl;

		cout << (s1 == s3) << endl;
		cout << (s1 > s3) << endl;
		cout << (s1 < s3) << endl;
	}

	//<< >> 的重载测试
	void TestString8()
	{
		string s1("hello");
		cout << s1 << endl;;

		string s2("hello world  haha");
		cout << s2 << endl;

		cin >> s2;
		cout << s2 << endl;

		getline(cin, s2);
		cout << s2 << endl;

	}

}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WoLannnnn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值