【C++】string类的模拟实现

前言:在上一篇中我们讲到了string类的使用方法,今天我们将进一步的去学习string类,去底层看看它顺带模拟实现部分的内容。

💖 博主CSDN主页:卫卫卫的个人主页 💞
👉 专栏分类:高质量C++学习 👈
💯代码仓库:卫卫周大胖的学习日记💫
💪关注博主和博主一起学习!一起努力!
在这里插入图片描述



模拟实现string类

string类的默认成员函数

class string
{
public:
	static const int npos;
	string(const char* str = "")//默认有参构造
		:_size(strlen(str))
	{
		_capacity = _size;//空间等于字节数+1
		_str = new char[_capacity + 1];//深度拷贝
		strcpy(_str, str);//作用域结束会自动调用析构
	}
	
	~string()//析构函数
	{
		delete[] _str;
		_str = nullptr;
		_capacity = 0;
		_size = 0;
	}
private:
	char* _str = nullptr;
	size_t _size = 0;//记录空间字符个数
	size_t _capacity = 0;//空间容量
};
static const int npos = -1; 

构造函数

  1. 我们在这里构造默认构造时候,我们之所以默认的是" "而不是’\0’和’空指针’,是为了防止strlen是的调用的时候对空指针的解引用。
  2. 开辟空间的时候要额外开辟一个,用于存放’\0’。
string(const char* str = "")//默认有参构造
	:_size(strlen(str))
{
	_capacity = _size;//空间等于字节数+1
	_str = new char[_capacity + 1];//深度拷贝
	strcpy(_str, str);//作用域结束会自动调用析构
}

拷贝构造函数

  1. 记住在string类拷贝构造的时候一定要是深拷贝,不然在出了作用域以后会自动调用析构函数,就会照成对一块空间进行两次析构。
  2. 开辟一块空间,让原本的对象指向新开辟的空间,再将拷贝对象的内容拷贝过来,就可以完成深度拷贝(目前我就写一个传统写法,现代写法后面会讲)了。
string(const string& s)//深度拷贝构造-传统写法
{
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}

析构函数

  1. 析构函数就比较简单了,对原本的指针的空间进行释放即可。
~string()//析构函数
{
	delete[] _str;
	_str = nullptr;
	_capacity = 0;
	_size = 0;
}

operator=赋值运算符重载

  1. 这里我们的赋值运算符重载同样的是一种深拷贝。
  2. 同理我们先开辟一块同样大小的空间,然后在把拷贝的内容拷贝过去
  3. 这里我需要强调一共点,至于为什么我们要开辟一块空间,因为这种写法我们可以理解成是最暴力最简单的,因为我们在检查空间容量的时候,没有缩容这个写方法,如果我们想让他直接对原本的空间进行扩容的话,我们还需要考虑原本的空间是否足够大和或者多了需要缩容,这种写法可以完美的解决这些问题。
string& operator=(const string& s)//赋值运算符重载-深度拷贝
{
	if (this != &s)//防止自己给自己赋值
	{
		char* tmp = new char[s._capacity + 1];
		strcpy(tmp, s._str);
		delete _str;
		_str = tmp;//深拷贝,通过新开辟的空间让,防止结束后被析构两次
		_capacity = s._capacity;
		_size = s._size;
	}
	return *this;
}

容量大小相关的函数

size( )函数

  1. 我们直接返回size中的个数即可,不包括\0
size_t size()const //返回字节数
{
	return _size;
}

capacity()函数

  1. 同理返回capacity中的个数即可
size_t capacity()const //返回内存容量
{
	return _capacity;
}

reserve()函数-查看空间是否足够,不够就扩容

  1. 我们先查看传过来的字符是否比空间容量大,大就扩容,小的话就不用管他
  2. 在C++中我们没有像rellaco这样的函数(不代表我们不能用,只是通常不喜欢用),我们就用new开辟一块同样大小的空间,然后拷贝过去即可。
void reserve(size_t n)//检查内存,查看是否n个字符能否存入该空间中
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//C++中没有rellaco这样的函数,所以通常不会直接追加
		strcpy(tmp, _str);//先把原本的字符拷贝到新开辟的空间
		delete[] _str;
		_str = tmp;
		_capacity = n;//空间扩容到n,记住扩容的时候size是没变的,size是查看有多少个有效字符
	}
}

resize()函数- 调整空间大小

  1. 我们需要查看调整的大小以后,是否比原来的空间大小还小,如果比原来的空间还小我们直接在n的位置赋值给’\0’即可,我们不需要考虑把后面的元素删了,因为我们调整了size以后无法访问到后面的元素。
  2. 如果需要调整的空间更大了,我们就i将后面的元素全部扩容成’\0’或者指定的元素,然后将最后一个元素赋值成’\0’。
void resize(size_t n, char ch = '\0')//扩容
{
	if (n < _size)
	{
		_str[n] = '\0';
		_size = n;
	}
	else
	{
		reserve(n);//查看是否需要扩容
		for (size_t i = _size; i < n; i++)
		{
			_str[i] = ch;//后面的元素如果没有指定就全部赋值成'\0'
		}
		_str[n] = '\0';//最后一个元素赋值成\0
		_size = n;
	}

}

函数测试

void test_string1()
{
	string s1("wei wei");
	cout << s1 << endl;
	s1.resize(10, 'z');
	cout << s1 << endl;
}

在这里插入图片描述


clear()函数

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

下标访问元素

  1. 因为对象中的_str就是一个指针,所以我们直接引用返回pos位置的值即可。
char& operator[](size_t pos)
{
	assert(pos < _size);//防止越界
	return _str[pos];//返回pos位置的引用,相当于就是把那个字符给返回了
}

//const版本
const char& operator[](size_t pos)
{
	assert(pos > 0 && pos < _size);
	return _str[pos];
}

函数测试

void test_string1()
{
	string s1("wei wei");
	cout << s1 << endl;
	s1.resize(10, 'z');
	cout << s1 << endl;
	cout << s1[2] << endl;
}

在这里插入图片描述


查找相关函数

find()函数 – 查找字符

  1. 我们先判断传过来的pos位置是不是在字符串的长度范围内。
  2. 我们通过半缺省,可以指定从pos位置查找,如果找到了返回Pos位置的下标即可,没找到返回npos(即我们前面定义的一个静态变量,-1);
size_t find(char ch, size_t pos = 0)//查找ch这个字符,pos指代从什么位置开始查找
{
	assert(pos < _size);
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
	return npos;
}

函数测试:

	void test_string1()
	{
		string s1("wei wei");
		cout << s1 << endl;
		s1.resize(10, 'z');
		cout << s1.find('w', 2) << endl;
	}

在这里插入图片描述


find()函数 – 查找字符串

  1. 同理我们先判断传过来的pos位置是不是在字符串的长度范围内。
  2. 我们通过半缺省,可以指定从pos位置查找,如果找到了返回Pos位置的下标即可,没找到返回npos(即我们前面定义的一个静态变量,-1);
  3. 但是注意我们这里找的是字符串,所以我们可以通过strstr这个库函数去帮助我们在pos的位置开始找。
  4. 如果找到了,strstr会返回起始位置的地址,因为我们只需要将原本主串的首地址减去字串的首地址就是中间元素的个数,也就是字串第一次出现的位置了。
  5. 没有找到我们同理返回npos(-1)。
size_t find(const char* str, size_t pos = 0)const //查找str这个字符串,pos指代从什么位置开始找
{
	assert(pos < _size);
	const char* p = strstr(_str + pos, str); // 在_str + pos的位置去找str, 如果找到了就返回的是str出现的位置, 否则返回空
	if (p)//找到了
	{
		return p - _str;//即返回字串第一次出现的位置,两个指针相减就是中间相差元素的个数
	}
	else
	{
		return npos;
	}
}

函数测试:

void test_string1()
{
	string s1("weiweizhou");
	cout << s1 << endl;
	s1.resize(10, 'z');
	cout << s1.find("zhou", 2) << endl;
}

在这里插入图片描述


迭代器相关的函数

  1. 在string类中,我们的底层实际上就是一种指针,我们这里把它typedef一下即可。
  2. 正向迭代器就是直接返回指针的起始地址。
  3. 反向迭代器就是返回最后一个字符的地址。
typedef char* iterator;
typedef const char* const_iterator;
const_iterator begin()const//正向迭代器 - 返回初始位置
{
	return _str;
}

const_iterator end()const //反向迭代器- 返回末端位置
{
	return _str + _size;
}

iterator begin()
{
	return _str;
}

iterator end()
{
	return _str + _size;
}

函数测试:

void test_string1()
{
	string s1("weiweizhou");
	string::iterator item;
	item = s1.begin();
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << *(item + i) << " ";
	}
	cout << endl;
	string::iterator end;
	end = s1.end();
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << *(end - i - 1) << " ";

	}

}

在这里插入图片描述


插入字符的相关函数

push_back(char ch)尾插字符

  1. 尾插字符实际就是和顺序表一样的写法,大家不懂的话可以看我之前的文章顺序表
  2. 我们先查看空间容量够不够如果不够就直接扩容。
  3. 我们直接找到最后一个\0的位置,直接替换成插入的字符即可。
  4. 插入以后将size++,在让最后一个元素题换成\0
void push_back(char ch)//尾插字符
{
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);//和顺序表一样的写法
	}
	_str[_size] = ch;
	++_size;
	_str[_size] = '\0';
}

函数测试:

void test_string1()
{
	string s1("weiweizhou");
	cout <<"追加前: " << s1 << endl;
	s1.push_back('c');
	cout <<"追加后: " << s1 << endl;
}

在这里插入图片描述


void append(const char* str)追加字符串

  1. 同理我们先查看追加字符串后的空间够不够,如果不够同理我们得先扩容。
  2. 将追加的字符串通过库函数直接拷贝到原本的字符串的’\0’的位置即可。
  3. 最后调整_size的大小。
void append(const char* str)//追加字符串
{
	size_t len = strlen(str);
	if (_capacity < _size + len)//查看追加后是否需要扩容
	{
		reserve(_size + len);
	}
	strcpy(_str + _size, str);//尾插到之前\0的位置
	_size += len;
}

函数测试:

void test_string1()
{
	string s1("weiweizhou");
	cout <<"追加前: " << s1 << endl;
	s1.append(" hello");
	cout <<"追加后: " << s1 << endl;
}

在这里插入图片描述


string& operator +=(const char* str)尾插字符串

  1. 我们前面写了一个append函数,直接在这里调用即可。
  2. 需要注意的是我们这里记得引用返回,不然无法连续赋值,在前面运算符重载种讲过了,这里也就不会在提了(这里就不给大家测试了)。
string& operator +=(const char* str)//尾插字符串
{
	append(str);
	return *this;
}

string& operator +=(const char ch)尾插字符

  1. 同理我们前面写了一个push_back尾插字符,这里也是直接调用就好啦。
  2. 这里也同样引用返回。
string& operator +=(const char ch)//尾插字符
{
	push_back(ch);
	return *this;
}

void insert(size_t pos, const char ch)指定位置插入字符

  1. 同理我们先查看插入以后是否空间容量足够,不够就扩容。
  2. 我们将pos位置后面的值全部往后移动一个,然后在将插入的字符放在pos位置即可,不懂的看下图。
  3. 这里作者认为最难的就是控制循环结束条件了,这里我们需要知道是插入到pos的位置,因此当end走到pos的位置时候即停止循环,进行插入。在这里插入图片描述
void insert(size_t pos, const char ch)//指定位置插入字符
{
	assert(pos <= _size);
	if (_capacity == _size)//查看追加后是否需要扩容
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);//和顺序表一样的写法
	}
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];//将pos后的字符全部移动到插入后的位置
		end--;
	}
	_str[pos] = ch;
	_size += 1;
}

函数测试:

void test_string1()
{
	string s1("weiweizhou");
	cout <<"追加前: " << s1 << endl;
	s1.insert(1, 'c');
	//s1.append(" hello");
	cout <<"追加后: " << s1 << endl;
}

在这里插入图片描述


void insert(size_t pos, const char* str)指定位置插入字符串

  1. 同理我们依然先判断容量够不够,如果不够依然是扩容那套方法。
  2. 这次我们需要注意的是追加是字符串而不是单单一个字符了。
  3. 因为追加的是一个字符串,我们这里让pos位置以后的字符全部往后移动len个(插入字符串的长度),然后将字符串插入到Pos的位置即可。(如下图所示)

在这里插入图片描述

void insert(size_t pos, const char* str)//指定位置插入字符串
{
	assert(pos <= _size);
	size_t len = strlen(str);
	if (_capacity < _size + len)//查看追加后是否需要扩容
	{
		reserve(_size + len);
	}
	size_t end = _size + len; //找到最后一个位置
	while (end > pos + len - 1)//当End走到插入字符串的最后一个时候,全部移动完成
	{
		_str[end] = _str[end - len];//将pos后的字符串全部移动到插入后的位置
		end--;
	}
	strncat(_str + pos, str,len); // 拷贝到_str + pos的位置
	_size = _size + len;
}

函数测试:

void test_string1()
{
	string s1("weiweizhou");
	cout <<"追加前: " << s1 << endl;
	s1.insert(2, "dapang");
	//s1.append(" hello");
	cout <<"追加后: " << s1 << endl;
}

在这里插入图片描述


删除字符和字符串

void erase(size_t pos, size_t len = npos)从pos的位置往后删len个字符

  1. 在删除我们考虑两种情况,一种是使用的人并没有指定删除多长的字符,就默认把它全部删除。 第二种就是使用的人选择删除的长度比他从pos位置指定的后面的所有的字符删除都长。对于这两种情况,我们都可以直接在pos的位置赋值为’\0’,然后改变size的大小就行。
    2.如果指定删除的长度,小于Pos后面所有字符串的长度,我们只需要把pos位置加上要删除的长度这一块空间后面的字符串拷贝到pos的位置来即可。(如下图)在这里插入图片描述
void erase(size_t pos, size_t len = npos)//从pos的位置往后删len个字符,len默认是-1
{
	assert(pos < _size);
	if (pos == npos || len > _size - pos)//如果没有指定pos,或者从pos位置往后删除的长度比整个字符串都长,就默认全部删除
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size = _size - len;
	}
}

函数测试:

void erase(size_t pos, size_t len = npos)//从pos的位置往后删len个字符,len默认是-1
{
	assert(pos < _size);
	if (pos == npos || len > _size - pos)//如果没有指定pos,或者从pos位置往后删除的长度比整个字符串都长,就默认全部删除
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size = _size - len;
	}
}

在这里插入图片描述


查找字串

string substr(size_t pos = 0, size_t len = npos)找字串

  1. 通过半缺省,如果不指定pos就从开始开始找,一直往后len个都是属于字串。如果不知道len,就是从pos位置一直到最后都是字串。
  2. 同理我们需要判断从读取的长度是不是比Pos位置后面所有的字符都要长,如果是的那么从pos位置开始,后面都是字串,全部保存到我们的对象中即可。
  3. 如果没有比pos后面的字符长,我们只需要加到pos+len的位置就停下来即可。
  4. 我们这里注意返回的是一个对象。
string substr(size_t pos = 0, size_t len = npos)//找字串
{
	string sub;
	if (len >= _size - pos)//如果读取的长度比pos往后的所有字符加起来都长,就后面所有的都是字串
	{
		for (size_t i = pos; i < _size; i++)
		{
			sub += _str[i];
		}
	}
	else
	{
		for (size_t i = pos; i < len + pos; i++)
		{
			sub += _str[i];
		}
	}
	return  sub;
}

函数测试:

void test_string1()
{
	string s1("weiweizhou");
	cout <<"主串: " << s1 << endl;
	string sub = s1.substr(3, 3);
	cout <<"子串: " << sub << endl;
}

在这里插入图片描述


流插入 流提取 重载

流提取

  1. 在C++中,可以将流插入和流提取操作符(<<和>>)重载为string类的成员函数,也可以将它们重载为全局函数。选择将它们重载为成员函数还是全局函数取决于设计者的个人偏好和具体需求。
  2. 将流插入和流提取操作符重载为成员函数的一个优势是可以直接访问类的私有成员和保护成员。这意味着在重载的函数中可以直接使用类的成员变量和成员函数,而无需通过访问器或其他方式来获取或修改数据。这种直接访问对于操作字符串类中的数据非常方便。但是这种会破坏程序的封装性,不推荐。
  3. 另一方面,将流插入和流提取操作符重载为全局函数可以提高代码的可读性和灵活性。全局函数可以将string类的实例作为参数,并具有更通用的用法。例如,可以使用全局函数重载流插入操作符来将string对象插入到任何类型的输出流中,而不仅仅是标准输出流。同样,可以使用全局函数重载流提取操作符从任何类型的输入流中提取字符串。
ostream& operator<<(ostream& out, const string& s)
{
	for (auto ch : s)//out是内置类型数据,可以直接输出类型
	{
		out << ch;
	}
	return out;
}

流插入

  1. 我们先开辟一个数组,在我们读取数据后,如果数据不超过128个就先全部存放在这个数组中。
  2. 当数组中元素到了128个,我把数组中的数据转移到对象中,在把数组中的数据给清0即可。
  3. 如果数组本身就读不到128个在数据读取完成后,直接放在对象中即可。
istream& operator>>(istream& in, string& s)
{
	s.clear();
	char ch;
	ch = in.get();
	char buff[128];//开辟一个数组,将一开始读取的数据全部放在数组中
	size_t i = 0;
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		if (i == 127)//数组超过了127个以后,再将读取的数据放在对象中
		{
			buff[127] = '\0';
			s += buff;
			i = 0;
		}
		ch = in.get();
	}
	if (i > 0)//如果不足128就直接通过数组放在了对象中
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

函数测试:

void test_string1()
{
	string s2;
	cin >> s2;
	cout <<"输入的数据: " << s2 << endl;
}

在这里插入图片描述


getline输入数据

  1. 这里我们和刚刚流插入一样的玩法就可以了,不懂的就看上面的讲解就行了。
istream& getline(istream& in, string& s)
{
	s.clear();
	char ch;
	ch = in.get();
	char buff[128];
	size_t i = 0;
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[127] = '\0';
			s += buff;
			i = 0;
		}
		ch = in.get();
	}
	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

关系运算符重载

1.关于关系运算符重载由于比较简单,这里作者就不全部详细讲解了。

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

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

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

bool operator<=(const bit::string& s1, const bit::string& s2)
{
	return s1 < s2 || s1 == s2;
}

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

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


全部接口的整体代码

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

namespace bit
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
		static const int npos;
		const_iterator begin()const//正向迭代器 - 返回初始位置
		{
			return _str;
		}

		const_iterator end()const //反向迭代器- 返回末端位置
		{
			return _str + _size;
		}

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		string(const char* str = "")//默认有参构造
			:_size(strlen(str))
		{
			_capacity = _size;//空间等于字节数+1
			_str = new char[_capacity + 1];//深度拷贝
			strcpy(_str, str);//作用域结束会自动调用析构
		}

		~string()//析构函数
		{
			delete[] _str;
			_str = nullptr;
			_capacity = 0;
			_size = 0;
		}
		string(const string& s)//深度拷贝构造-传统写法
		{
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}

		const char* c_str()const  //以C语言的形式返回字符串
		{
			return _str;
		}

		string& operator=(const string& s)//赋值运算符重载-深度拷贝
		{
			char* tmp = new char[s._capacity + 1];
			strcpy(tmp, s._str);
			delete _str;
			_str = tmp;//深拷贝,通过新开辟的空间让_Str拷贝两次,防止结束后被析构两次
			_capacity = s._capacity;
			_size = s._size;
			return *this;
		}

		size_t size()const //返回字节数
		{
			return _size;
		}

		size_t capacity()const //返回内存容量
		{
			return _capacity;
		}
		const char& operator[](size_t pos)
		{
			assert(pos > 0 && pos < _size);
			return _str[pos];
		}

		void reserve(size_t n)//检查内存,查看是否n个字符能否存入该空间中
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];//C++中没有rellaco这样的函数,所以通常不会直接追加
				strcpy(tmp, _str);//先把原本的字符拷贝到新开辟的空间
				delete[] _str;
				_str = tmp;
				_capacity = n;//空间扩容到n,记住扩容的时候size是没变的,size是查看有多少个有效字符
			}
		}

		void resize(size_t n, char ch = '\0')//扩容
		{
			if (n < _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);//查看是否需要扩容
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;//后面的元素如果没有指定就全部赋值成'\0'
				}
				_str[n] = '\0';//最后一个元素赋值成\0
				_size = n;
			}

		}

		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 (_capacity < _size + len)//查看追加后是否需要扩容
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);//尾插到之前\0的位置
			_size += len;
		}

		string& operator +=(const char* str)//尾插字符串
		{
			append(str);
			return *this;
		}

		string& operator +=(const char ch)//尾插字符
		{
			push_back(ch);
			return *this;
		}

		void insert(size_t pos, const char ch)//指定位置插入字符
		{
			assert(pos <= _size);
			if (_capacity == _size)//查看追加后是否需要扩容
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);//和顺序表一样的写法
			}
			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];//将pos后的字符全部移动到插入后的位置
				end--;
			}
			_str[pos] = ch;
			_size += 1;
		}
		void insert(size_t pos, const char* str)//指定位置插入字符串
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (_capacity < _size + len)//查看追加后是否需要扩容
			{
				reserve(_size + len);
			}
			size_t end = _size + len; //找到最后一个位置
			while (end > pos + len - 1)//当End走到插入字符串的最后一个时候,全部移动完成
			{
				_str[end] = _str[end - len];//将pos后的字符串全部移动到插入后的位置
				end--;
			}
			strncpy(_str + pos, str,len); // 拷贝到_str + pos的位置
			_size = _size + len;
		}

		void erase(size_t pos, size_t len = npos)//从pos的位置往后删len个字符,len默认是-1
		{
			assert(pos < _size);
			if (pos == npos || len > _size - pos)//如果没有指定pos,或者从pos位置往后删除的长度比整个字符串都长,就默认全部删除
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size = _size - len;
			}
		}

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

		void swap(string& x, string& y)
		{
			x.swap(y);
		}

		size_t find(char ch, size_t pos = 0)const //查找ch这个字符,pos指代从什么位置开始查找
		{
			assert(pos < _size);
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}
			return npos;
		}

		size_t find(const char* str, size_t pos = 0)const //查找str这个字符串,pos指代从什么位置开始找
		{
			assert(pos < _size);
			const char* p = strstr(_str + pos, str); // 在_str + pos的位置去找str, 如果找到了就返回的是str出现的位置, 否则返回空
			if (p)//找到了
			{
				return p - _str;//即返回字串第一次出现的位置,两个指针相减就是中间相差元素的个数
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos = 0, size_t len = npos)//找字串
		{
			string sub;
			if (len >= _size - pos)//如果读取的长度比pos往后的所有字符加起来都长,就后面所有的都是字串
			{
				for (size_t i = pos; i < _size; i++)
				{
					sub += _str[i];
				}
			}
			else
			{
				for (size_t i = pos; i < len + pos; i++)
				{
					sub += _str[i];
				}
			}
			return  sub;
		}

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

	private:
		char* _str = nullptr;
		size_t _size = 0;
		size_t _capacity = 0;
	};
	const int string::npos = -1;

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

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

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

	bool operator<=(const bit::string& s1, const bit::string& s2)
	{
		return s1 < s2 || s1 == s2;
	}

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

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

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

	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char ch;
		ch = in.get();
		char buff[128];
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}

	istream& getline(istream& in, string& s)
	{
		s.clear();
		char ch;
		ch = in.get();
		char buff[128];
		size_t i = 0;
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[127] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}

好啦,今天的内容就到这里啦,下期内容预告vector的使用,博主最近比较忙可能更新的会比较慢请见谅


结语:今天的内容就到这里吧,谢谢各位的观看,如果有讲的不好的地方也请各位多多指出,作者每一条评论都会读的,谢谢各位。


🌏🗺️ 这里祝各位接下来的每一天好运连连 💞💞
  • 35
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 29
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

卫卫周大胖;

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

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

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

打赏作者

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

抵扣说明:

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

余额充值