string模拟实现

前言

上一期我们对STL进行了简单的介绍以及学习了string常用API的基本使用!本期我们来探索它的底层实现!自己对string的常用的接口进行模拟实现!

本期内容介绍

常用成员函数模拟实现

常用非成员函数模拟实现

成员函数

构造函数

在进行模拟实现前我们得先定义一下底层吧!它的底层其实很简单,一个字符指针、一个容量(不包含\0)、一个字符的个数!和我们数据结构介绍的差不多!不同的是这是类的属性所以我们搞成私有的就可以了!!

char* _str;
size_t _size;
size_t _capacity;

这里的构造最常用的是空构造和一个常量字符串构造!可以两个函数实现,但这里最好的方式是利用缺省参数,空构造就是只有一个表示结束的\0而字符串的结尾默认有一个\0,所以只需给一个空的字符串当缺省参数即可!

实现思路:size和capacity是当前常量的长度,开比容量多1的空间(存放\0的)给_str,然后把常量字符串str的数据给拷贝到_str!

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

拷贝构造

实现思路:给_str开字符串的容量+1的空间,然后把字符串的数据拷贝到_str,_str的size和capacity都和字符串的相等!

string(const string& str)
{
	_str = new char[str._capacity + 1];//多开一个存\0
	strcpy(_str, str.c_str);

	_size = str._size;
	_capacity = str._capacity;
}

赋值拷贝

实现思路:开一块和字符串str一样大的空间(包含\0)tmp,把str的数据拷贝给tmp,然后释放掉this的那块空间,在让this的_str指向tmp的空间,_size和_capacity都等于字符串的即可

string& operator=(const string& str)
{
	if (this != &str)//防止自己给自己赋值
	{
		char* tmp = new char[str._capacity + 1];//多开一个存\0
		strcpy(tmp, str._str);
		delete[] _str;
		_str = tmp;

		_size = str._size;
		_capacity = str._capacity;
	}

	return *this;
}

析构函数

实现思路:释放掉(delete[])空间,然后将size和capacity置为0

~string()
{
	delete[] _str;//释放空间
	_size = _capacity = 0;//属性置为0
}

c_str

实现思路:因为底层就是C语言的指针,所以直接返回底层的指针即可!由于只是返回不惜要修改,所以用const修饰

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

这个上面已经用过了,这里不在演示了!

迭代器

日常开发中正向迭代器用的居多,一般如果反向遍历的时候,因为string支持[]所以不用反向迭代器,所以这里只模拟实现正向的和const正向的!

实现思路:

因为底层是连续的,所以可以直接返回底层的指针_str作为begin,_str +_size就是end。如果是const的不让修改直接在后面加上const。

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

OK,我们在使用的时候就说过,支持迭代器就支持范围for,因为范围for的底层就是迭代器的替换,我们先来看看这里可不可以支持范围for?

OK,没问题!我们这可以通过看反汇编验证范围for的底层是否真的是迭代器的替换:

而且自己实现的迭代器的命名要和库里面的一致范围for才可以替换!我们可以换个名字试试:

这里只是把begin换成了Begin他就不认识了!!!所以范围for就是傻瓜式的替换迭代器!!!

size

实现思路:size是返回元素的个数,这里直接返回_size即可,又因为不需要修改所以可加上const

size_t size() const
{
	return _size;
}

capacity

实现原理:和上面的size一样,直接返回_capacity即可,因为不需要修改加上const

size_t capacity() const
{
	return _capacity;
}

我们在实现构造的时候size和capacity是等于常量字符串的长度的!所以这里相等!

empty

实现原理:直接判断size 是否等于0,不需要修改_size所以可以加const

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

clear

实现原理:由于只是把字符清理了,不清理空间!所以直接把_str[0]的位置置为\0即可!然后size 置为0!

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

reserve

实现思路:这个函数的作用是预开空间!所以我们只需开指定大小的空间把原来的_str的数据拷贝到新空间即可!(但注意的是到多开一个存\0)然后将原来的空间释放,让_str指向新的空间,新的_capacity就是n。因为reserve不支持缩容,所以我们只有在n>capacity的时候才开空间!!!!

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

		_str = tmp;
		_capacity = n;
	}
}

resize

实现思路:当n小于size时,保存前n个元素也就是在n下标处直接处理成\0(这里只是减小size不是缩容),如果是n > size的情况的话可能要扩容(n > capacity时扩,size < n < capacity不扩),而上面的reserve是当n>capacity时才扩容,所以我们在n>=size时直接先调用一下reserve,然后就一定有空间插入了!!最后再把要插入的字符尾插到n

void resize(size_t n, char c = '\0')
{
	if (n < _size)
	{
		_str[n] = '\0';
		_size = n;
	}
	else
	{
		reserve(n);
		for (size_t i = _size; i < n; i++)
		{
			_str[i] = c;
		}
		_str[n] = '\0';//记得最后补一个\0
	}
}

operator[]

[]是有const和非const的,这里都得来实现一下!具体原因在上一期已经说了,权限问题!

实现思路:我们返回pos位置元素的引用即可!但注意的是要对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];
}

front

实现思路:返回_str[0]的元素即可

char front()
{
	return _str[0];
}

back

实现思路:因为_size是最后一个元素的下一个位置,所以最后一个元素是_size-1,即返回_str[_size-1]的元素即可!

char back()
{
	return _str[_size - 1];
}

push_back

实现思路:在_size位置把\0替换成要插入的字符,然后在让_size++,最后补上个\0即可!但注意插入的时候一定要注意:有空间才能插入,所以在插入前一定要检查是否要扩容,我们这里的扩容可以是2倍也可以是1.5倍,如果正是个空串,我们第一次扩容的时候给他4个空间即可!

void push_back(char c)
{
	//判断是否扩容
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	//插入
	_str[_size++] = c;
	_str[_size] = '\0';
}

append

实现思路:将要插入的字符串可已利用strcpy拷贝到_size的位置!最后记得_size += len 

void append(const char* str)
{
	int len = strlen(str);
	if (len + _size > _capacity)
	{
		reserve(len + _size);
	}

	strcpy(_str + _size, str);
	_size += len;
}

operator+=

实现思路:直接复用push_back和append,最后返回*this即可

insert

这个可以在pos下标处插入一个字符,也可以在pos位置插入一个字符串!

实现思路:

插入一个元素:从最后一个元素的下一个位置(\0开始挪动)开始到pos位置的元素都依次往后挪动一个位置!挪完后再把插入的元素插入即可!

插入一个字符串:从_size往后len 个位置即end=_size +len开始依次将end-len 的元素往后挪到end 的位置, 挪动pos +len - 1个元素,然后再把字符串插入进去即可!

void insert(size_t pos, char c)
{
	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;
	}

	_str[pos] = c;
	_size++;
}

这里插入单个元素的话您的想法可能和我的不一样!例如你是从左后一个元素往后挪的!这个思路没问题!!!但是这种思路写的时候有坑,有时候不好检查!我再来把这种思路写一下:


void insert(size_t pos, char c)
{
	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] = c;
	_size++;
}

这样写看似是正确的!但在0下标处插入的时候会死循环!看结果:

原因我就不带大家调试看了,大家可以自己调试看!你会看到-1>0!!!没错,你没看错这段代码调试就是-1>0原因是end是size_t无符号整型,等减到0后就又开始从最大开始了!!!所以死循环!你可能会想把end换成int可以了吧,但是你不要忘了pos也是size_t的,C\C++是支持隐式类型转换的!低的会向高转!!所以不行,这里的解决方案有两种:一种是你把pos换成Int但是不推荐因为人家库里面是size_t 第二种是:你把end换成int把pos在循环挪动的时候强转成int!!!

void insert(size_t pos, char c)
{
	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] = c;
	_size++;
}

OK,我们继续来实现在pos位置插入一个字符串!!!

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

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

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

erase

实现思路:从指定的pos+len的位置开始往前依次挪动len 和即可!

void erase(size_t pos = 0, size_t len = npos)
{
	assert(pos < _size);
	if (len == pos || len  > _size - pos)//这里的len + pos > _size得写成这样防止溢出报错
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		for (size_t i = pos; i < _size; i++)
		{
			_str[i] = _str[i + len];
		}

		_size -= len;
		_str[_size] = '\0';
	}
}

当然你可以这样移动,也可以直接调用C语言的库函数strcpy直接把pos 到pos+len的len个字符移动到pos位置!

void erase(size_t pos = 0, size_t len = npos)
{
	assert(pos < _size);
	if (len == pos || len > _size - pos)//这里的len + pos > _size得写成这样防止溢出报错
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
}

pop_back

这里的实现思路有两种:1、可以直接调用erase  2、判断_size如果不为0则将_size--然后再将\0放到_size即可

void pop_back()
{
	assert(_size > 0);//确保有元素可删
	erase(_size-1, 1);
}

void pop_back()
{
	assert(_size > 0);//确保有元素可删
	--_size;
	_str[_size] = '\0';
}

swap

这个swap和算法库里面的不一样!算法库里面的那个会形成拷贝,代价较大!这个我们使用的时候介绍过他是直接交换指针的!

实现思路:我们可以把两个字符串对象的属性通过库函数的 swap给依次交换了!而一个属性至多8个字节(64位下指针为8个字节)拷贝代价小了很多!!!

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

OK,再来通过调试看看空间和size

find

find可以查找一个字符,也可以找一个字符串!找到了返回第一个字符的下标否则返回npos

实现思路:

找一个字符:遍历一遍字符串,逐一比较一遍,找到了返回下标,最后没找到返回npos

找一个字符串:可以利用KMP算法,但是KMP说实话在日常开发中的效率不咋地。我们这里可以利用C语言的时候的一个函数strstr,如果他返回的是空指针直接返回npos,否则返回,strstr返回的指针减去_str就是第一次出现的下标!

size_t find(char c, size_t pos = 0)
{
    assert(pos < _size);//保证查找的位置合法
	for (size_t i = pos; i < _size; i++)
	{
		if (c == _str[i])
			return i;
	}

	return npos;
}

size_t find(const char* str, size_t pos = 0)
{
    assert(pos < _size);//保证查找的位置合法
	const char* ret = strstr(_str, str);
	return ret == nullptr ? npos : ret - _str;
}

substr

实现思路:如果是pos位置开始的长度比_size大的话那就直接从pos位置开始把所有的字符都给子串,否则的话就把pos位置开始后的len个长度的字符给子串!

string substr(size_t pos = 0, size_t len = npos)
{
	string sub;
	if (len >= _size - pos)
	{
		for (size_t i = pos; i < _size; i++)
		{
			sub += _str[i];
		}
	}
	else
	{
		for (size_t i = pos; i < pos + len; i++)
		{
			sub += _str[i];
		}
	}

	return sub;
}

这里介绍了swap后就可以介绍一下拷贝构造和赋值拷贝的现代写法了!上面的那个时传统写法!

拷贝构造的现代写法

string(const string& str)
{
	string tmp(str.c_str());
	swap(tmp);
}

这里起始就是很好的利用了swap,先让一个字符串tmp来用str.c_str()来构造,然后再让this与tmp交换资源即可实现拷贝构造!!!

赋值拷贝的现代写法

string& operator=(string str)
{
	swap(str);
	return *this;
}

这里也是直接把形参的引用去掉,让其形成是实参的拷贝,然后让this与str直接交换即可达到赋值的目的!!!

非成员函数

逻辑比较相关

这里的逻辑比较主要有6个,分别是: > >= < <= == !=六个,我们可以实现两个然后其他复用即可!例如可以实现>和==

实现思路:

>  : 我们可以利用C语言的库函数strcmp根据它的返回值来判断是否大于,

== :和上面的同理!判断返回值是否==0

bool operator>(const string& s1, const string& s2)
{
	int ret = strcmp(s1.c_str(), s2.c_str());
	return ret > 0;
}

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

OK,其他的直接复用即可!大于并且等于就是>=,不是大于等于就是<,不是大于就是<=,不是等于就是不等于!

bool operator>=(const string& s1, const string& s2)
{
	return (s1 > s2) && (s1 == s2);
}

bool operator<(const string& s1, const string& s2)
{
	return !(s1 >= s2);
}

bool operator<=(const string& s1, const string& s2)
{
	return !(s1 > s2);
}

bool operator!=(const string& s1, const string& s2)
{
	return !(s1 == s2);
}

swap

这里有个swap的原因是防止误操作的!因为即使类成员提供了swap但是有时候不排除操作失误去掉算法库里面的那个函数!因此设计者考虑到了这一点,于是在全局重载了算法库的那个swap!这有朋友会想调用的时候难道编译器不会去和算法库的swap错调吗?显然是不会的!因为算法库的那个是模板,调用时需要实例化,而这个是已经实例化好的!编译器有最优匹配原则的即有现成吃现成,否则在实例化!这就和人一样,假设你今天在家学习一天饿了,有两种选择一是拿着你妈留的钱去买饭或点外卖;另一是自己做!你肯定是拿着钱 点外卖嘛~!

实现思路:直接用成员函数swap即可!

void swap(string& s1, string& s2)
{
	s1.swap(s2);
}

operator<<

实现思路:虽然<<不支持自定义类型,但支持内置类型!字符串中是一个个的字符,所以直接将字符串的内容按字符逐个输出,最后返回ostream的流对象即可!

这里有个问题就是:为什么最后还要返回ostream对象呢?原因很简单:因为把当前内容插入到输出流中后还有可能继续向流中插入其他东西!例如插入完字符串后还有可能插入endl去换行!!!!即是为了来连续的输出!!!

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

operator>>

实现思路:我们可以逐个获取字符,然后将这些字符尾插到字符串中!但是cin 和C语言的scanf是读取不到\n和空格的这就会导致一直不结束等着你输入!所以不能直接用它两读取字符,可以用C语言的getchar但这里不推荐,虽然C++兼容C语言但它两的缓冲区毕竟不一样,在有些极端情况下会有bug,所以在这里推荐使用C++istream的get方法来读取,这个可以读取到空格和\n

istream& operator>>(istream& in, string& str)
{
	str.clear();//流提取本质是一种覆盖,下面是直接尾插的,所以这里要清空一下
	char c;
	c = in.get();
	while (c != ' ' && c != '\n')
	{
		str += c;
		c = in.get();
	}

	return in;
}

OK这里可以与STL库中的对比一下:

显然效果一样!!!这里还有的人觉得这样实现不太好!原因是每次读取一个字符都要频繁的尾插到后面,也就是要频繁的扩容!效率大打折扣!所以有人进行了对这种方法的优化!

优化思路:用一个定长的数组来先把字符从起来,最后当满了之后一次性尾插,这样就大大的减少了扩容到来的消耗!即使你比较短或前几次的内容已经为尾插到了str里面,但时短的不足数组长度的这些也是直接可以判断尾插到后面的!

istream& operator>>(istream& in, string& str)
{
	str.clear();//流提取本质是一种覆盖,下面是直接尾插的,所以这里要清空一下
	char c;
	c = in.get();
	char buff[128];
	size_t i = 0;
	while (c != ' ' && c != '\n')
	{
		buff[i++] = c;
		if (i == 127)
		{
			buff[i] = '\0';
			str += buff;//当够127个字符时时将其添上\0并尾插到后面!
			i = 0;//为继续插入准备
		}
		c = in.get();
	}

	if (i > 0)//当兵输入的较短时,直接把当前的字符串尾插到str
	{
		buff[i] = '\0';
		str += buff;
	}

	return in;
}

getline

实现思路:和>>的实现原理基本一致!!我们只需要判断当c等于\n的时候结束掉即可!!!

istream& getline(istream& in, string& str)
{
	str.clear();//流提取本质是一种覆盖,下面是直接尾插的,所以这里要清空一下
	char c;
	c = in.get();
	while (c != '\n')
	{
		str += c;
		c = in.get();
	}

	return in;
}

istream& getline(istream& in, string& str)
{
	str.clear();//流提取本质是一种覆盖,下面是直接尾插的,所以这里要清空一下
	char c;
	c = in.get();
	char buff[128];
	size_t i = 0;
	while (c != '\n')
	{
		buff[i++] = c;
		if (i == 127)
		{
			buff[i] = '\0';
			str += buff;//当够127个字符时时将其添上\0并尾插到后面!
			i = 0;//为继续插入准备
		}
		c = in.get();
	}

	if (i > 0)//当兵输入的较短时,直接把当前的字符串尾插到str
	{
		buff[i] = '\0';
		str += buff;
	}

	return in;
}

全部源码:


#pragma once

#include <assert.h>

namespace cp
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

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

		/*string(const string& str)//传统写法
		{
			_str = new char[str._capacity + 1];//多开一个存\0
			strcpy(_str, str._str);

			_size = str._size;
			_capacity = str._capacity;
		}*/

		string(const string& str)//现代写法
		{
			string tmp(str.c_str());
			swap(tmp);
		}
		 
		/*string& operator=(const string& str)//传统写法
		{
			if (this != &str)//防止自己给自己赋值
			{
				char* tmp = new char[str._capacity + 1];//多开一个存\0
				strcpy(tmp, str._str);
				delete[] _str;
				_str = tmp;

				_size = str._size;
				_capacity = str._capacity;
			}

			return *this;
		}*/

		string& operator=(string str)//现代写法
		{
			swap(str);
			return *this;
		}

		~string()
		{
			delete[] _str;//释放空间
			_size = _capacity = 0;//属性置为0
		}

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

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

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

		void clear()
		{
			_str[0] = '\0';
			_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 c = '\0')
		{
			if (n < _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = c;
				}
				_str[n] = '\0';//记得最后补一个\0
			}
		}

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

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

		char front()
		{
			return _str[0];
		}

		char back()
		{
			return _str[_size - 1];
		}

		void push_back(char c)
		{
			//判断是否扩容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			//插入
			_str[_size++] = c;
			_str[_size] = '\0';
		}

		void append(const char* str)
		{
			int len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}

			strcpy(_str + _size, str);
			_size += len;
		}

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

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

		void insert(size_t pos, char c)
		{
			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;
			}

			_str[pos] = c;
			_size++;
		}

		/**void insert(size_t pos, char c)
		{
			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] = c;
			_size++;
		}*/

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

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

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

		/**void erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);
			if (len == pos || len  > _size - pos)//这里的len + pos > _size得写成这样防止溢出报错
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				for (size_t i = pos; i < _size; i++)
				{
					_str[i] = _str[i + len];
				}

				_size -= len;
				_str[_size] = '\0';
			}
		}*/

		void erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);
			if (len == pos || len > _size - pos)//这里的len + pos > _size得写成这样防止溢出报错
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
		}

		void pop_back()
		{
			assert(_size > 0);//确保有元素可删
			erase(_size-1, 1);
		}

		/*void pop_back()
		{
			assert(_size > 0);//确保有元素可删
			--_size;
			_str[_size] = '\0';
		}*/

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

		size_t find(char c, size_t pos = 0)
		{
			assert(pos < _size);//保证查找的位置合法
			for (size_t i = pos; i < _size; i++)
			{
				if (c == _str[i])
					return i;
			}

			return npos;
		}

		size_t find(const char* str, size_t pos = 0)
		{
			assert(pos < _size);
			const char* ret = strstr(_str, str);
			return ret == nullptr ? npos : ret - _str;
		}

		string substr(size_t pos = 0, size_t len = npos)
		{
			string sub;
			if (len >= _size - pos)
			{
				for (size_t i = pos; i <= _size; i++)
				{
					sub += _str[i];
				}
			}
			else
			{
				for (size_t i = pos; i < pos + len; i++)
				{
					sub += _str[i];
				}
			}

			return sub;
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;

		const static size_t npos = -1;//注意这里是特例,一般的我们前面介绍的static成员都是在类里面声明,类外面定义,这里是对这个开了个特权
	};

	bool operator>(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret > 0;
	}

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

	bool operator>=(const string& s1, const string& s2)
	{
		return (s1 > s2) && (s1 == s2);
	}

	bool operator<(const string& s1, const string& s2)
	{
		return !(s1 >= s2);
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return !(s1 > s2);
	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

	void swap(string& s1, string& s2)
	{
		s1.swap(s2);
	}

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

	/*istream& operator>>(istream& in, string& str)
	{
		str.clear();//流提取本质是一种覆盖,下面是直接尾插的,所以这里要清空一下
		char c;
		c = in.get();
		while (c != ' ' && c != '\n')
		{
			str += c;
			c = in.get();
		}

		return in;
	}*/

	/*istream& getline(istream& in, string& str)
	{
		str.clear();//流提取本质是一种覆盖,下面是直接尾插的,所以这里要清空一下
		char c;
		c = in.get();
		while (c != '\n')
		{
			str += c;
			c = in.get();
		}

		return in;
	}*/


	istream& operator>>(istream& in, string& str)
	{
		str.clear();//流提取本质是一种覆盖,下面是直接尾插的,所以这里要清空一下
		char c;
		c = in.get();
		char buff[128];
		size_t i = 0;
		while (c != ' ' && c != '\n')
		{
			buff[i++] = c;
			if (i == 127)
			{
				buff[i] = '\0';
				str += buff;//当够127个字符时时将其添上\0并尾插到后面!
				i = 0;//为继续插入准备
			}
			c = in.get();
		}

		if (i > 0)//当兵输入的较短时,直接把当前的字符串尾插到str
		{
			buff[i] = '\0';
			str += buff;
		}

		return in;
	}

	istream& getline(istream& in, string& str)
	{
		str.clear();//流提取本质是一种覆盖,下面是直接尾插的,所以这里要清空一下
		char c;
		c = in.get();
		char buff[128];
		size_t i = 0;
		while (c != '\n')
		{
			buff[i++] = c;
			if (i == 127)
			{
				buff[i] = '\0';
				str += buff;//当够127个字符时时将其添上\0并尾插到后面!
				i = 0;//为继续插入准备
			}
			c = in.get();
		}

		if (i > 0)//当兵输入的较短时,直接把当前的字符串尾插到str
		{
			buff[i] = '\0';
			str += buff;
		}

		return in;
	}
}

OK,好兄弟本期内容就介绍到这里!我们下期再见~!

结束语:

我不相信寒冬已至,因为火把在我手中!

  • 36
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值