C++ string模拟实现

目录

  • 模拟实现string的结构
  • 接口函数的实现
    • 构造函数和析构函数
    • 迭代器的实现
    • operator[]
    • reserve和resize
    • 三种尾插函数
    • insert
    • find
    • erase
    • substr
    • 赋值重载
    • 拷贝构造
    • 比较大小
    • 流提取,流插入
  • 完整代码

模拟实现string的结构

前面我们知道了string的结构比较复杂,这里我们实现简单一点的

string类的成员变量有
char* _str是指向堆中字符串的指针
_size表示有效字符的长度
_capacity表示空间容量

这里的实现方式类似于顺序表

我们还需定义一个静态成员npos
同时,我们也要把自己实现string类放在一个命名空间my_string

此时,就有了一个初步的一个框架:

namespace my_string
{
	class string
	{
		public:
		//成员函数
		private:
			char* _str;
			size_t _size;
			size_t _capacity;
		public:
			const static size_t npos;
	};
	const size_t string::npos = -1;
	
}

接口函数的实现

现在我们有三个成员变量,就可以实现3个函数:c_str(),size()capacity()

const char* c_str() const
{
	return _str;
}

size_t size() const
{
	return _size;
}

size_t capacity() const
{
	return _capacity;
}

这三个函数不会修改成员函数,所以可以用const修饰this指针

构造函数和析构函数

这里我们只实现无参构造和用字符串构造
完全可以把这两种写成一个含有缺省参数的构造函数

string(const char* str = "")
{
    _size = strlen(str);
	_capacity = _size;
	_str = new char[_capacity + 1];
	memmove(_str, str, _size + 1);
}

_size的值就是str的长度,_capacity的值和_size相同
然后new出_capacity+1大小的空间

之所以要开辟_capacity大小的空间,是因为_size表示有效字符长度,_capacity表示可容纳有效字符的容量,都不包含’\0',但是在存储上还是需要存储'\0‘的,所以要在底层多开辟一个字符空间

然后这里最主要的一处就是memmove(_str, str, _size + 1)
把str中的值拷贝到新空间中,这是深拷贝

如果写成_str = str就成为浅拷贝了,后面析构时就会发生一块空间析构2次,报错

析构函数

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

迭代器的实现

string里的迭代器其实就是指针,我们可以把char*typedefiterator,const迭代器同理

typedef char* iterator;
typedef const char* const_iterator;

然后实现begin()end()

iterator begin()
{
	return _str;
}

iterator end()
{
	return _str + _size;
}

const_iterator begin()const
{
	return _str;
}

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

因为范围for的底层也是调用迭代器,所以此时实现了迭代器之后,我们的string也支持范围for了


operator[]

[]运算符的重载需要重载2个函数,一个函数是即可读也可写,一个是只可以读

这里我们可以用assert断言判断一下pos是否合法

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

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

reserve和resize

void reserve(size_t capacity)
{
	if (capacity > _capacity)
	{
		char* tmp = new char[capacity + 1];
		memmove(tmp, _str,_size+1);
		delete[] _str;
		_str = tmp;
		_capacity = capacity;
		tmp = nullptr;
	}
}

这里需要注意的一点是,把_str中的数据拷贝到tmp中时,需要使用memcpy或者memmove函数,虽然_strtmp是指向字符串的,本应使用strcpy但是strcpy会拷贝直到'\0',如果字符串中间有字符'\0'的话,数据挪动只挪动到'\0',会有数据缺失
所以必须要用memcpymrmmove,以_size+1为终止条件

所以要注意,在模拟实现string中,要用memcpymemmove替代所有strcpy

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

三种尾插函数

push_back尾插一个字符
需要先判断容量是否满,如果满了要扩容,我们这里指定的扩容策略是:二倍扩容。并且如果当前容量为0,就设置容量为4
接着就在_str[_size]处放入字符c,再在它后面加上\0

void push_back(char c)
{
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	
	_str[_size] = c;
	_size++;
	_str[_size] = '\0';

}

下面是2个append函数,一种参数是string对象,一种是字符串

首先判断是否需要扩容,接着就深拷贝数据过去

void append(const string& str)
{
	size_t len = str.size();
	if (size() + len > capacity())
	{
		reserve(size() + len);
	}
//	strcpy(_str+size(),str._str);
	memmove(_str + _size, str._str,len+1);
	_size += len;
}

void append(const char* c)
{
	size_t len = strlen(c);
	if (len + size() > capacity())
	{
		reserve(size() + len);
	}
	/*strcpy(_str + size(), c);*/
	memmove(_str + _size, c,len+1);
	_size += len;

}

operator+=函数
operator+=函数可以复用前面的push_back函数和append函数

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

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

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

insert

先实现插入n个字符chinsrt函数

首先还是判断是否需要扩容

assert(pos <= size());
if (n + size() > capacity())
{
	reserve(n + size());
}

接着需要挪动数据,把pos包括pos后面的数据向后挪动n个距离

size_t end = size();			
while (pos <= end)
{
	_str[end + n] = _str[end];
	end--;
}

这里其实有个错误,在判断条件中需要加上end != npos
因为end的类型为size_t无符号整形
假如pos的值为0时,pos==end==0时循环继续,end减去1后变为整形的最大值,仍然大于pos,死循环
所以才要加上判断end != npos

size_t end = size();
while (pos <= end && end != npos)
{
	_str[end + n] = _str[end];
	end--;
}

接着就是插入n个字符ch

for (int i = 0; i < n; i++)
{
	_str[pos + i] = ch;
}
_size += n;

完整代码:

void insert(size_t pos, size_t n, char ch)
{
	assert(pos <= size());
	if (n + size() > capacity())
	{
		reserve(n + size());
	}

	size_t end = size();

	//这里判断条件多了end != npos,是因为end的类型是size_t,无符号整形
	//假如pos的值为0时,pos==end==0时循环继续,end减去1后变为整形的最大值,仍然大于pos,死循环
	//所以才要加上判断end != npos
	while (pos <= end && end != npos)
	{
		_str[end + n] = _str[end];
		end--;
	}

	for (int i = 0; i < n; i++)
	{
		_str[pos + i] = ch;
	}
	_size += n;

}

然后我们就可以根据上面的写法,完成插入字符串和插入一个string对象的insert函数

void insert(size_t pos, const string& str)
{
	assert(pos <= size());
	int len = str.size();
	if (len + size() > capacity())
	{
		reserve(len + size());
	}

	size_t end = size();
	while (pos <= end && end != npos)
	{
		_str[end + len] = _str[end];
		end--;
	}

	for (int i = 0; i < len; i++)
	{
		_str[pos + i] = str._str[i];
	}

	_size += len;

}

void insert(size_t pos, const char* str)
{
	assert(pos <= size());

	int len = strlen(str);
	if (len + size() > capacity())
	{
		reserve(len + size());
	}

	size_t end = size();

	while (pos <= end && end != npos)
	{
		_str[end + len] = _str[end];
		end--;
	}

	const char* cur = str;
	int i = 0;
	while (*cur != '\0')
	{
		_str[pos + i] = *cur;
		cur++;
		i++;
	}
	
	_size += len;
}

find

这里实现查找一个字符和查找一个字符串的find函数

查找一个字符很简单,遍历一遍,看有没有和要查找一样的字符的,如果找到,返回下标,如果没找到,就返回npos

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

	return npos;
}

查找一个字符串:
我们使用C中的strstr查找字符串,如果找到了,会返回指向找到的字符串首个元素的指针,否则返回NULL

找到时,需要返回字符位置,这里可以使用指向同一块空间上的2个指针相减,就可以得到中间的元素个数,也就是字符位置的下标

size_t find(const char* str, size_t pos = 0)
{
	assert(pos < size());
	char* rp = strstr(_str + pos, str);
	if (rp == NULL)
	{
		return npos;
	}
	else
	{
		return rp - _str;
	}
}

erase

void erase(size_t pos, size_t n = npos)

如果n的值为npos或者pos+n>size(),就表示删除pos位置(包括pos)后所有字符

我们就直接把_str[pos]值改为'\0',接着改变_size的值为pos就可以

剩下的情况就是正常删除就可以了

void erase(size_t pos, size_t n = npos)
{
	assert(pos <= size());

	if (n == npos || pos + n > size())
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		int cur = pos + n;
		while (cur != size() + 1)
		{
			_str[pos] = _str[cur];
			pos++;
			cur++;
		}
		_size -= n;
	}
}

substr

string substr(size_t pos = 0,size_t len  =npos) const

如果len==npospos+len>size()就表示取从pos位置后所有字符

string substr(size_t pos = 0,size_t len  =npos) const
{
	assert(pos < _size);
	size_t n = len;
	if (len == npos || pos + len > _size)
	{
		n = _size - pos;
	}

	string tmp;
	tmp.reserve(n);
	for (size_t i = pos; i < n + pos; i++)
	{
		tmp += _str[i];
	}

	return tmp;
}

赋值重载

赋值重载有2中写法,一种是传统写法,一种是现代写法

传统写法很简单,就是实现一个深拷贝就可以了

string& operator=(const string& s)
{
	if (this != &s)
	{
		char* tmp = new char[s._capacity + 1];
		memmove(tmp, s._str, s._size + 1);
		delete[] _str;
		_str = tmp; 
		_size = s._size;
		_capacity = s._capacity;
	}
	return *this;
}

现代写法的思想:
a = b;是一个赋值重载的调用,以往在赋值重载中需要写将b的_size,_capacity拷贝给a

最主要的时,需要先delete掉a._str在堆上的空间,再开辟一个和b._str一样的空间给a

那么在函数已经拷贝出了一个临时对象tmp,既然这个临时对象在函数结束后会自动销毁,不如交换this和tmp的_str各个成员变量的值

这样在函数结束后,tmp自动被销毁,tmp._str指向的空间被销毁,这块空间就是之前this->_str指向的空间
我们不必再去手动销毁这段空间了

C++ string模拟实现_第1张图片

string& operator=(const string& s)
{
	if (this != &s)
	{
		string tmp(s);

		std::swap(_str, tmp._str);
		std::swap(_size, tmp._size);
		std::swap(_capacity, tmp._capacity);
	}

	return *this;
}

我们也可以把三个std::swap函数封装起来写成一个函数

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

string& operator=(const string& s)
{
	if (this != &s)
	{
		string tmp(s);

		std::swap(_str, tmp._str);
		std::swap(_size, tmp._size);
		std::swap(_capacity, tmp._capacity);
	}

	return *this;
}

下面还有一种更简单的写法:

上面的那个写法还需创建一个tmp临时对象,这里直接用一个非引用类型的形参
这个形参在函数结束后也就自行调用析构函数被清理了

string& operator=(string tmp)
{
	swap(tmp);

	return *this;
}

拷贝构造

拷贝构造也有传统写法和现代写法

//拷贝构造函数传统写法
string(const string& s)
{
	_str = new char[s._capacity + 1];
	//strcpy(_str, s._str);
	memmove(_str, s._str, s._size + 1);
	_size = s._size;
	_capacity = s._capacity;
}

拷贝构造函数现代写法

这种写法的拷贝构造需要写初始化列表,对this的成员进行初始化
因为对于拷贝构造对于成员的默认初始化,在不同的平台,不同版本的编译器是不同的,有可能会进行初始化,也有可能不会初始化
如果没有进行初始化,和tmp swap完之后tmp的成员都是随机值
然后tmp调用析构函数时,销毁一块随机空间,会报错

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

而且对于现代写法,遇到中间有’\0’的字符串,只会拷贝到’\0’以前,所以对于拷贝构造,传统写法更好一点


比较大小

字符串是根据每个字符的ASCII来判断大小

我们逐个字符判断大小

while (i1 < _size && i2 < s._size)
{
	if (_str[i1] < s._str[i2])
	{
		return true;
	}
	else if (_str[i1] > s._str[i2])
	{
		return false;
	}
	else
	{
		i1++;
		i2++;
	}
}

然后如果知道某一字符串遍历完了还没有判断出哪个字符串小,我们就需要判断字符串长度来判断,字符串长的就大
比如:“hello”就比"helloworld"小

bool operator<(const string& s)const
{
	size_t i1 = 0;
	size_t i2 = 0;
	while (i1 < _size && i2 < s._size)
	{
		if (_str[i1] < s._str[i2])
		{
			return true;
		}
		else if (_str[i1] > s._str[i2])
		{
			return false;
		}
		else
		{
			i1++;
			i2++;
		}
	}
	
	if (_size < s._size)
	{
		return true;
	}
	else
	{
		return false;
	}
}

operator==:

bool operator==(const string& s)const
		{
			return _size == s._size && memcmp(_str, s._str, _size)==0;
		}

接着就可以复用这2个函数,完成其余比较大小的运算符重载

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 || *this == s;
}

流提取,流插入

流提取,流插入我们要写在类外


std::ostream& operator<<(std::ostream& out, const my_string::string& str)
{
	for (auto e : str)
	{
		out << e;
	}
	return out;

	//C的字符数组,以\0算长度
	//string不看\0,以_size为终止长度
}
void clear() //在流提取重载中清理缓冲区使用
{
		_str[0] = '\0';
		_size = 0;
}

std::istream& operator>>(std::istream& in, my_string::string& str)
{
	str.clear();//清除掉同一个string对象中缓冲区中的内容


	//使用in.get()是因为,get()函数可以读取任何字符包括:' '和'\n'
	//而使用in>>ch读到' '或 '\n'时会卡住(阻塞)
	char ch = in.get();

	//下面这么写是处理前缓冲区前面的空格或者换行
	//保证输入:"空格空格空格空格hello" 的时候可以正常读取到"hello"
	//以及输入"换行换行换行换行换行hello" 时可以正常读取到"hello"
	while (ch == ' ' || ch == '\n')
	{
		ch = in.get();
	}

	//设置一个char类型的数组,是因为如果没读取一个字符就+=到str中,会频繁开空间,有消耗
	//在栈上开辟一个数组消耗小,所以先把读取的字符放到buff里,等buff"满"的时候,=再把这个数组+=到str中
	char buff[128];
	int i = 0;

	//每读取到一个字符,如果不是空格或换行,就+=到string里,继续读取
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;

		//如果buff"满"的时候,要在最后加上'\0',把buff加到str里
		//此时还需继续读取,就把i赋为0,让buff再从头存储
		if (i == 127)
		{
			buff[i] = '\0';
			str += buff;
			i = 0;
		}
		//str += ch;
		ch = in.get();
	}


	//如果读取的字符个数没有超过128,同时也读取到了字符,就在当前i下标处赋'\0'
	//把buff加到str中
	if (i != 0)
	{
		buff[i] = '\0';
		str += buff;
	}


	return in;
}

完整代码

#pragma once
#include 
#include

namespace my_string
{
	class string
	{

	public:


		typedef char* iterator;
		typedef const char* const_iterator;
		//范围for的底层其实就是使用迭代器,所以这里实现了迭代器之后,就自然而然地支持范围for循环了


		string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			//strcpy(_str, str);
			memmove(_str, str, _size + 1);
		}


		//拷贝构造函数传统写法
		string(const string& s)
		{
			_str = new char[s._capacity + 1];
			//strcpy(_str, s._str);
			memmove(_str, s._str, s._size + 1);
			_size = s._size;
			_capacity = s._capacity;
		}



		//拷贝构造函数现代写法

		//这种写法的拷贝构造需要写初始化列表,对this的成员进行初始化
		//因为对于拷贝构造对于成员的默认初始化,在不同的平台,不同版本的编译器是不同的,有可能会进行初始化,也有可能不会初始化
		//如果没有进行初始化,和tmp swap完之后tmp的成员都是随机值
		//然后tmp调用析构函数时,销毁一块随机空间,会报错

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

		//而且对于现代写法,遇到中间有'\0'的字符串,只会拷贝到'\0'以前
		//所以对于拷贝构造,传统写法更好一点


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

		const char* c_str() const
		{
			return _str;
		}

		size_t size() const
		{
			return _size;
		}

		size_t capacity() const
		{
			return _capacity;
		}


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

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

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		const_iterator begin()const
		{
			return _str;
		}

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

		void reserve(size_t capacity)
		{
			if (capacity > _capacity)
			{
				char* tmp = new char[capacity + 1];
				//strcpy(tmp, _str);
				memmove(tmp, _str,_size+1);
				delete[] _str;
				_str = tmp;
				_capacity = capacity;
				tmp = nullptr;
			}
		}

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


		void push_back(char c)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			
			_str[_size] = c;
			_size++;
			_str[_size] = '\0';

		}

		void append(const string& str)
		{
			size_t len = str.size();
			if (size() + len > capacity())
			{
				reserve(size() + len);
			}
		//	strcpy(_str+size(),str._str);
			memmove(_str + _size, str._str,len+1);
			_size += len;
		}

		void append(const char* c)
		{
			size_t len = strlen(c);
			if (len + size() > capacity())
			{
				reserve(size() + len);
			}
			/*strcpy(_str + size(), c);*/
			memmove(_str + _size, c,len+1);
			_size += len;

		}

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

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

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

		void insert(size_t pos, const string& str)
		{
			assert(pos <= size());
			int len = str.size();
			if (len + size() > capacity())
			{
				reserve(len + size());
			}

			size_t end = size();
			while (pos <= end && end != npos)
			{
				_str[end + len] = _str[end];
				end--;
			}

			for (int i = 0; i < len; i++)
			{
				_str[pos + i] = str._str[i];
			}

			_size += len;

		}

		void insert(size_t pos, const char* str)
		{
			assert(pos <= size());

			int len = strlen(str);
			if (len + size() > capacity())
			{
				reserve(len + size());
			}

			size_t end = size();

			while (pos <= end && end != npos)
			{
				_str[end + len] = _str[end];
				end--;
			}

			const char* cur = str;
			int i = 0;
			while (*cur != '\0')
			{
				_str[pos + i] = *cur;
				cur++;
				i++;
			}
			
			_size += len;
		}


		void insert(size_t pos, size_t n, char ch)
		{
			assert(pos <= size());
			if (n + size() > capacity())
			{
				reserve(n + size());
			}

			size_t end = size();

			//这里判断条件多了end != npos,是因为end的类型是size_t,无符号整形
			//假如pos的值为0时,pos==end==0时循环继续,end减去1后变为整形的最大值,仍然大于pos,死循环
			//所以才要加上判断end != npos
			while (pos <= end && end != npos)
			{
				_str[end + n] = _str[end];
				end--;
			}

			for (int i = 0; i < n; i++)
			{
				_str[pos + i] = ch;
			}
			_size += n;

		}

		void erase(size_t pos, size_t n = npos)
		{
			assert(pos <= size());

			if (n == npos || pos + n > size())
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				int cur = pos + n;
				while (cur != size() + 1)
				{
					_str[pos] = _str[cur];
					pos++;
					cur++;
				}
				_size -= n;
			}
		}
		
		size_t find(char ch,size_t pos  = 0)
		{
			assert(pos < size());
			for (int i = pos; i < size(); i++)
			{
				if (_str[i] == ch)
					return i;
			}

			return npos;
		}

		size_t find(const char* str, size_t pos = 0)
		{
			assert(pos < size());
			char* rp = strstr(_str + pos, str);
			if (rp == NULL)
			{
				return npos;
			}
			else
			{
				return rp - _str;
			}
		}
		
		string substr(size_t pos = 0,size_t len  =npos) const
		{
			assert(pos < _size);
			size_t n = len;
			if (len == npos || pos + len > _size)
			{
				n = _size - pos;
			}

			string tmp;
			tmp.reserve(n);
			for (size_t i = pos; i < n + pos; i++)
			{
				tmp += _str[i];
			}

			return tmp;
		}


		/*string& operator=(const string& s)
		{
			if (this != &s)
			{
				char* tmp = new char[s._capacity + 1];
				memmove(tmp, s._str, s._size + 1);
				delete[] _str;
				_str = tmp; 
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}*/


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

		//赋值重载现代写法
		
		// a = b;是一个赋值重载的调用,以往在赋值重载中需要写将b的_size,_capacity拷贝给a
		// 最主要的时,需要先delete掉a._str在堆上的空间,再开辟一个和b._str一样的空间给a
		// 那么在函数已经拷贝出了一个临时对象tmp,既然这个临时对象在函数结束后会自动销毁,不如交换this和tmp的_str各个成员变量的值
		// 这样在函数结束后,tmp自动被销毁,tmp._str指向的空间被销毁,这块空间就是之前this->_str指向的空间
		// 我们不必再去手动销毁这段空间了
		// 
		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		string tmp(s);

		//		std::swap(_str, tmp._str);
		//		std::swap(_size, tmp._size);
		//		std::swap(_capacity, tmp._capacity);

		//		//swap(tmp);
		//	}

		//	return *this;

		//}


		//这种写法更加简介
		//上面的那个写法还需创建一个tmp临时对象,这里直接用一个非引用类型的形参
		//这个形参在函数结束后也就自行调用析构函数被清理了
		string& operator=(string tmp)
		{
			swap(tmp);

			return *this;
		}

		void clear() //在流提取重载中清理缓冲区使用
		{
			_str[0] = '\0';
			_size = 0;
		}


		bool operator<(const string& s)const
		{
			size_t i1 = 0;
			size_t i2 = 0;
			while (i1 < _size && i2 < s._size)
			{
				if (_str[i1] < s._str[i2])
				{
					return true;
				}
				else if (_str[i1] > s._str[i2])
				{
					return false;
				}
				else
				{
					i1++;
					i2++;
				}
			}
			
			if (_size < s._size)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		
		bool operator==(const string& s)const
		{
			return _size == s._size && memcmp(_str, s._str, _size)==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 || *this == s;
		}


	private:

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

	const size_t string::npos = -1;


	std::ostream& operator<<(std::ostream& out, const my_string::string& str)
	{
		for (auto e : str)
		{
			out << e;
		}
		return out;

		//C的字符数组,以\0算长度
		//string不看\0,以_size为终止长度
	}


	std::istream& operator>>(std::istream& in, my_string::string& str)
	{
		str.clear();//清除掉同一个string对象中缓冲区中的内容


		//使用in.get()是因为,get()函数可以读取任何字符包括:' '和'\n'
		//而使用in>>ch读到' '或 '\n'时会卡住(阻塞)
		char ch = in.get();

		//下面这么写是处理前缓冲区前面的空格或者换行
		//保证输入:"空格空格空格空格hello" 的时候可以正常读取到"hello"
		//以及输入"换行换行换行换行换行hello" 时可以正常读取到"hello"
		while (ch == ' ' || ch == '\n')
		{
			ch = in.get();
		}

		//设置一个char类型的数组,是因为如果没读取一个字符就+=到str中,会频繁开空间,有消耗
		//在栈上开辟一个数组消耗小,所以先把读取的字符放到buff里,等buff"满"的时候,=再把这个数组+=到str中
		char buff[128];
		int i = 0;

		//每读取到一个字符,如果不是空格或换行,就+=到string里,继续读取
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;

			//如果buff"满"的时候,要在最后加上'\0',把buff加到str里
			//此时还需继续读取,就把i赋为0,让buff再从头存储
			if (i == 127)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
			//str += ch;
			ch = in.get();
		}


		//如果读取的字符个数没有超过128,同时也读取到了字符,就在当前i下标处赋'\0'
		//把buff加到str中
		if (i != 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return in;
	}
};

你可能感兴趣的:(C++,c++,stl,开发语言,对象)