【C++】模拟实现string




前言


模拟实现不是为了写得和库里面一样好。而是为了更好的了解底层,从而能够更熟练的使用这些类,同时也能学习大佬们的代码风格。

在学习这章之前,需要对类与对象有一定的知识基础,如果对类与对象有些生疏的话,可以看看这篇文章:《类与对象
string 类是 C++标准库中的一个重要类,用于表示字符串。

以下是一些关于 string 类的主要特点:

  1. 动态存储:可以自动管理内存,根据字符串的实际长度动态分配和释放内存。
  2. 丰富的操作:提供了很多方便的操作方法,如字符串连接、查找、比较、提取子串等。
  3. 高效性:在性能上表现较好,适用于各种字符串处理场景。


成员变量


在正式模拟实现之前,我们得要先确定我们要实现的类中的成员变量由哪些类型构成:

  1. 首先需要有一个字符指针,用于存储字符串。
  2. 再定义一个变量,用来记录字符串的有效个数。
  3. 再定义一个变量,用来记录该字符指针能存储的有效字符个数大小
  4. 再定义一个全局变量npos,来完成以下操作:
    • npos是一个静态成员常量值,具有size_t类型元素的最大可能值。
    • 当在string的成员函数中作为len(或sublen)参数的值时,该值表示“直到字符串结束”。
    • 作为返回值,它通常用于表示没有匹配。
    • 该常量定义为-1,由于size_t是无符号整型,因此它是该类型的最大可表示值。
class string
{

private:
	char* _str = nullptr;  // 字符指针, 字符串用于存储字符串
	size_t _size = 0; // 用于记录该字符串有几个有效字符(即字符串的长度)
	size_t _capacity = 0; // 用于记录该字符指针能存储的有效字符个数大小

	// Member constants:
	const static size_t npos = -1;
};


成员函数


在这里插入图片描述

构造函数

空字符串构造函数(默认构造函数)
构造一个长度为0个字符的空字符串。

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

拷贝构造函数

这里需要先介绍两个概念:

浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我夺,玩具损坏。在这里插入图片描述

深拷贝

在这里插入图片描述
可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。

拷贝构造函数实现

构造一个str的副本,这里因为涉及到资源管理的问题,所以拷贝构造时,我们需要重新创建一个空间,来存储str的内容。否则如果直接浅拷贝的话,会导致一下两个问题:

  1. 对其中一个字符串改变时,另一个字符串也会跟着被改变(因为它们管理的是同一片空间)
  2. 在调用析构函数时,同一份资源被释放两次
	string(const string& str)
	{
		_str = new char[str._capacity + 1];
		strcpy(_str, str._str);
		_size = str._size;
		_capacity = str._capacity;
	}

string构造函数不止有这两个,如果想要去实现其他的,也不是很困难。只是这两个构造函数,在大部分的情况下就够用了。

析构函数

对于这些参与空间资源分配的类,我们都要自己写析构函数,否则很可能导致内存泄漏。

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

赋值重载

为字符串赋一个新值,替换其当前内容(如果是两个相等的字符串就没必要相互赋值了)。

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

这里还有一种写法,我们可以复用拷贝构造函数:

	string& operator=(const string& str)
	{
		if (str != this->_str)
		{
			string tmp = str;
			swap(tmp);
		}
		return *this;
	}

在这里插入图片描述
当然这里的swap函数是需要我们自己实现的:

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

这里我复用了std空间里面的swap函数,将string类中的成员变量都交换掉。


空间管理函数


在这里插入图片描述
这里只模拟实现我勾选的这些函数。
实现这些功能并不复杂,代码如下:

	size_t capacity() const
	{
		return _capacity;
	}
	size_t size() const
	{
		return _size;
	}
	void reserve(size_t n = 0)
	{
		assert(_str);
		// 我实现的是只考虑扩容的,不考虑缩容
		if (_capacity < n)
		{
			char* tmp = new char[n + 1]; // 开辟空间
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}
	void resize(size_t n, char c = '\0')
	{
		assert(_str);
		// 不考虑缩容的情况
		if (n > _capacity)
		{
			reserve(n);
			for (size_t i = _size;i < n;++i)
				_str[i] = c;
			_str[_size = n] = '\0';
		}
	}
	void clear()
	{
		_str[_size = 0] = '\0';
	}
	bool empty() const
	{
		return _size == 0;
	}


元素访问

在这里插入图片描述

要实现[]重载并不复杂,但是需要写两个,一个是可读可写的,一个是只读的:

	char& operator[](size_t n)
	{
		assert(n >= 0);
		assert(n < _size);
		return _str[n];
	}
	const char& operator[](size_t n) const
	{
		assert(n >= 0);
		assert(n < _size);
		return _str[n];
	}


元素修改

在这里插入图片描述

这里有些函数我是重载了几个版本的,具体实现细节我写进注释里了

	void push_back(char c)
	{
		assert(_str);
		// 先判断是否需要扩容
		if (_size == _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 10 : 2 * _capacity;
			reserve(newcapacity);
		}
		_str[_size] = c;
		_str[++_size] = '\0'; // 增加字符以后不要忘记将_size增加
	}
	string& append(const string& str)
	{
		assert(_str && str._str);
		// 先判断是否需要扩容
		size_t len = strlen(str._str);
		if (_size + len >= _capacity)
		{
			size_t newcapacity = _size + len + 10; // 每次扩容都多增加是个容积的容错
			reserve(newcapacity);
		}
		memmove(_str + _size, str._str, len);
		_str[_size += len] = '\0';
		return *this;
	}
	string& operator+=(char c)
	{
		push_back(c);
		return *this;
	}
	string& operator+=(const string& str)
	{
		append(str);
		return *this;
	}
	string& insert(size_t pos, const string& str)
	{
		assert(pos <= _size);
		size_t len = strlen(str._str);
		if (_size + len >= _capacity)
			reserve(_size + len);
		size_t end = _size + len;
		while (end - len + 1 > pos)
		{
			_str[end] = _str[end - len];
			--end;
		}
		memmove(_str + pos, str._str, len);
		_size += len;
		return *this;
	}
	string& insert(size_t pos, char c)
	{
		assert(pos <= _size);
		if (_size >= _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 10 : 2 * _capacity;
			reserve(newcapacity);
		}
		size_t end = _size + 1;
		while (end > pos)
		{
			_str[end] = _str[end - 1];
			--end;
		}
		_str[pos] = c;
		++_size;
		return *this;
	}
	string& erase(size_t pos = 0, size_t len = npos)
	{
		assert(pos < _size);
		if (len == npos || pos + len >= _size)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			size_t end = pos + len;
			while (end <= _size)
			{
				_str[end - len] = _str[end];
				++end;
			}
			_size -= len;
		}
		return *this;
	}
	void swap(string& str)
	{
		std::swap(_str, str._str);
		std::swap(_size, str._size);
		std::swap(_capacity, str._capacity);
	}


字符串运算


在这里插入图片描述

c_strdata的功能都是一样的。这两个函数是为了能够让string类兼容C语言中的一些函数。比如:我们使用C语言中的fopen函数打开一个文件名(文件名是一个常量字符串),而假设我们使用string类来管理文件名,这是,就需要这两个函数来将string类转换成常量字符串了。

	const char* c_str()
	{
		return _str;
	}
	const char* data()
	{
		return _str;
	}
	size_t find(const string& str, size_t pos = 0) const
	{
		char* tmp = strstr(_str + pos, str._str);
		if (tmp == nullptr) // 查找子串失败
			return npos;
		return tmp - _str; // 返回两个指针之间的间隔距离,即表示找到的子串的起始位置
	}
	size_t find(char c, size_t pos = 0) const
	{
		for (size_t i = 0;i < _size;++i)
			if (_str[i] == c) return i;
		return npos;
	}
	size_t rfind(char c, size_t pos = 0) const
	{
		for (size_t i = _size - 1;i != npos;--i)
			if (_str[i] == c) return i;
		return npos;
	}
	string substr(size_t pos = 0, size_t len = npos) const
	{
		assert(pos < _size);
		string str;
		if (len == npos || pos + len >= _size)
			len = _size;
		for (size_t i = pos;i < len;++i)
			str += _str[i];
		return str;
	}


流提取 & 流插入


流提取

因为在STL中string类的对象是可以直接使用cout打印出来的,如果我们也想让我们自己实现的string能够直接打印出来,就需要重载一下<<操作符,具体操作如下:

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

流插入

STL中string类也是支持cin插入数据的,如果我们也想让我们自己实现的string能够使用cin插入数据,就需要重载一下>>操作符,具体操作如下:

std::istream& operator>>(std::istream& in, string& str)
{
	str.clear();
	char ch = in.get();
	while (ch != ' ' && ch != '\n')
	{
		str += ch;
		ch = in.get();
	}
	return in;
}

我们需要先将原string对象中的资源清理一下,再插入。这里是利用了istream类中的get()函数从输入缓冲区获取我们输入的字符,然后再使用我们在上面重载的+=操作符,来将我们输入的一组数据插入到一个string对象中。

但这里存在一个问题:
就是如果我们每次读取一个字符,再用+=这个操作符时,假设输入缓冲区中的数据量很大,那么必定存在很多次扩容,扩容还是会有一定的消耗的,所以我们使用一个字符数组来充当缓冲区,现将字符读入到这个数组中,待数组满了以后,我们再将这个数组中的数据插入到string对象中,这样就能有效的降低扩容的次数了

std::istream& operator>>(std::istream& in, string& str)
{
	str.clear();
	char buffer[128] = {};
	int count = 0;
	for (char ch = in.get();ch != ' ' && ch != '\n';ch = in.get())
	{
		buffer[count++] = ch;
		if (count == 127)
		{
			str += buffer;
			count = 0;
		}
	}
	if (count > 0)
	{
		buffer[count] = '\0';
		str += buffer;
	}
	return in;
}

这里还需要重载一个函数getline()获取一行字符串。因为>>操作符,遇到空格或者回车就结束读取了,如果我们需要读取一行字符串(里面可能含有空格),这是就需要使用getline()

std::istream& getline(std::istream& in, string& str)
{
	str.clear();
	char buffer[128] = {};
	int count = 0;
	for (char ch = in.get();ch != '\n';ch = in.get())
	{
		buffer[count++] = ch;
		if (count == 127)
		{
			str += buffer;
			count = 0;
		}
	}
	if (count > 0)
	{
		buffer[count] = '\0';
		str += buffer;
	}
	return in;
}


迭代器


迭代器的设计很巧妙的,不同的容器都能够通过相同的方式去访问,这就是迭代器的强大之处。下面就先通过string的迭代器带大家先了解一下迭代器,随着后面模拟实现其他容器,迭代器的结构也会相应的发生变化。

begin & end

在这里插入图片描述
这里只模拟实现我标记出来的那些,C++11出的那几个迭代器,主要是为了区分普通迭代器(iterator)和常量迭代器(const_iterator),但其实,我们用上面的那些就能够涵盖普通迭代器与常量迭代器了(因为普通迭代器和常量迭代器是可以重载的)

	typedef char* iterator;
	typedef const char* const_iterator;
	// 普通迭代器 (可读可写)
	iterator begin()	
	{
		return _str; // 返回字符串的第一个字符的位置
	}
	iterator end()
	{
		return _str + _size;  // 返回字符串的最后一个字符的下一个位置
	}
	// const迭代器 (只可读不可写)
	const_iterator begin() const
	{
		return _str;   // 返回字符串的第一个字符的位置
	}
	const_iterator end() const
	{
		return _str + _size;    // 返回字符串的最后一个字符的下一个位置
	}

因为string的底层空间是连续的,所以我们可以直接使用原生指针来定义为迭代器。
当然,使用范围for的前提是需要满足满足迭代器beginend,才能满足范围for
在这里插入图片描述
这里我只是将begin改为Begin,就不能支持范围for了

rbegin & rend

再说反向迭代器之前,需要具备适配器模式的知识,如果不是很了解可以先看看这篇文章:适配器模式

解释一下为什么要具有适配器模式的知识再往下看:
如果我们只是单纯的完成string类的反向迭代器的功能,其实就可以直接复制一遍正向迭代器的代码,改一下就能实现这个功能了。

但STL中有很多容器,每个容器的底层实现都不一样,所以C++中的迭代器主要有以下几种类型:

  1. 输入迭代器:只读,只能顺序向前移动。
  2. 输出迭代器:只写,只能顺序向前移动。
  3. 前向迭代器:可读写,能顺序向前移动。
  4. 双向迭代器:可读写,能顺序向前和向后移动。
  5. 随机访问迭代器:可读写,能跳跃式地访问元素。

有了适配器模式,我们写一份代码。就能让与该迭代器模式相似的容器中的反向迭代器复用这里的代码(比如,stringvector的迭代器都是支持随机访问的,所以我们写一份能够支持随机访问的反向迭代器的代码,就能使stringvector都能够复用)

这里使用了三个模板参数,目的如下:
在这里插入图片描述
在使用迭代器时,我们难免会使用->(结构体指针访问成员)操作符,或者使用*(解引用)操作符(关于这两个操作符的重载,在模拟实现list章节会重点讲,因为在stringvector的底层物理结构是连续的空间,我们用原生指针就能当做它的迭代器,所以不需要重载这两个操作符。但是我们在这里实现的反向迭代器不是原生指针,而是用一个结构体封装起来的结构,从上面实现可以看出。所以需要重载这两个操作符。这里只要先知道,重载->操作符的返回值是char*或者const char*,重载*操作符的返回值是char&或者const char&)。

因为 const char& 是只读的,而 char& 可读可写,同理char*可以改变所指向的内容,const char*不能改变所指向的内容。所以我们需要将两个迭代器区分开来,而我们嵌套三个模板参数,就可以用后面两个模板参数来区分了。这样,我们就只用写一份代码,const非const的迭代器就都能使用这一份代码了。我在这里用一个图给大家展示一下:
在这里插入图片描述
但是请注意:在string中,我们并不会用到->操作符,因为->操作符是自定义类型的指针对其成员的一个访问操作符,string类不会去存一个自定义类型,因为在后面模拟实现vector类的时候,反向迭代器我也会使用这段代码,所以这里我设计成三个模板参数。(实际上char&const char&是不用传递过来的,但为了能够使用这串代码传过来也一样的)

再来看看实现部分,这里我是模拟库里面实现了一个对称的结构:
在这里插入图片描述
所以我们在对迭代器进行解引用操作时,是对当前位置的前一个位置解引用(此时迭代器的位置不发生改变,所以我们需要创建一个迭代器用于记录当前位置的前一个位置,再对该位置解引用)。
在这里插入图片描述

而对于前置++/–,和后置++/–的实现,是利用正向迭代器向相反方向移动实现的:
在这里插入图片描述

具体一些操作实现如下:

	template<class Iterator, class Reference, class Point>
	struct __random_reverse_iterator
	{
		typedef __random_reverse_iterator self;
		__random_reverse_iterator(const Iterator& it)
			:_cur(it)
		{}
		Reference operator*()
		{
			Iterator tmp = _cur;
			return *(--tmp);
		}
		Point operator->()
		{
			return &(this->operator*());
		}
		self& operator++()
		{
			--_cur;
			return *this;
		}
		self operator++(int)
		{
			self tmp = *this;
			--_cur;
			return tmp;
		}

		self& operator--()
		{
			++_cur;
			return *this;
		}
		self operator--(int)
		{
			self tmp = *this;
			++_cur;
			return tmp;
		}

		self& operator+=(int nums)
		{
			_cur -= nums;
			return *this;
		}
		self& operator-=(int nums)
		{
			_cur += nums;
			return *this;
		}
		bool operator!=(const self& it)
		{
			return _cur != it._cur;
		}
		bool operator==(const self& it)
		{
			return _cur == it._cur;
		}
	private:
		Iterator _cur;
	};

// 下面是我们实现的string类(只含反向迭代器部分)
class string
{
public:
	typedef __random_reverse_iterator<iterator, char&, char*> reverse_iterator;
	typedef __random_reverse_iterator<const_iterator, const char&, const char*> const_reverse_iterator;
	reverse_iterator rbegin()
	{
		return reverse_iterator(end());
	}
	reverse_iterator rend()
	{
		return reverse_iterator(begin());
	}
	const_reverse_iterator rbegin() const
	{
		return const_reverse_iterator(end());
	}
	const_reverse_iterator rend() const
	{
		return const_reverse_iterator(begin());
	}
};
	

这里我并没有将这个反向迭代器的所用功能实现完,但是基本的框架也是有了。



总结


我在这里将我模拟实现string类的源码附在下面,供大家参考一下如何封装的(因为是在学习阶段,所以封装做得可能不是太好,以下只是我个人的理解):

#include <cstring>
#include <cassert>

namespace hyt
{
	template<class Iterator, class Reference, class Point>
	struct __random_reverse_iterator
	{
		typedef __random_reverse_iterator self;
		__random_reverse_iterator(const Iterator& it)
			:_cur(it)
		{}
		Reference operator*()
		{
			Iterator tmp = _cur;
			return *(--tmp);
		}
		Point operator->()
		{
			return &(this->operator*());
		}
		self& operator++()
		{
			--_cur;
			return *this;
		}
		self operator++(int)
		{
			self tmp = *this;
			--_cur;
			return tmp;
		}

		self& operator--()
		{
			++_cur;
			return *this;
		}
		self operator--(int)
		{
			self tmp = *this;
			++_cur;
			return tmp;
		}

		self& operator+=(int nums)
		{
			_cur -= nums;
			return *this;
		}
		self& operator-=(int nums)
		{
			_cur += nums;
			return *this;
		}


		bool operator!=(const self& it)
		{
			return _cur != it._cur;
		}
		bool operator==(const self& it)
		{
			return _cur == it._cur;
		}
	private:
		Iterator _cur;
	};


	class string
	{
	public: // 声明友元函数
		friend bool operator==(const string& lhs, const string& rhs);
		friend bool operator!=(const string& lhs, const string& rhs);
		friend bool operator>(const string& lhs, const string& rhs);
		friend bool operator>=(const string& lhs, const string& rhs);
		friend bool operator<(const string& lhs, const string& rhs);
		friend bool operator<=(const string& lhs, const string& rhs);
	public: // 迭代器
		typedef char* iterator;
		typedef const char* const_iterator;
		typedef __random_reverse_iterator<iterator, char&, char*> reverse_iterator;
		typedef __random_reverse_iterator<const_iterator, const char&, const char*> const_reverse_iterator;
		// 普通迭代器
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		// const迭代器
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}
		
		// 反向普通迭代器
		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}
		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}
		
		// 反向const迭代器
		const_reverse_iterator rbegin() const
		{
			return const_reverse_iterator(end());
		}
		const_reverse_iterator rend() const
		{
			return const_reverse_iterator(begin());
		}
	public:
		// 构造函数
		string(const char* str = "")
		{
			size_t size = strlen(str);
			_size = _capacity = size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		// 拷贝构造
		string(const string& str)
		{
			_str = new char[str._capacity + 1];
			strcpy(_str, str._str);
			_size = str._size;
			_capacity = str._capacity;
		}
		// 赋值操作符重载
		string& operator=(const string& str)
		{
			if (str != this->_str)
			{
				string tmp = str;
				swap(tmp);
			}
			return *this;
		}

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

		// Capacity:
		size_t capacity() const
		{
			return _capacity;
		}
		size_t size() const
		{
			return _size;
		}
		void reserve(size_t n = 0)
		{
			assert(_str);
			// 我实现的是只考虑扩容的,不考虑缩容
			if (_capacity < n)
			{
				char* tmp = new char[n + 1]; // 开辟空间
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}
		void resize(size_t n, char c = '\0')
		{
			assert(_str);
			// 不考虑缩容的情况
			if (n > _capacity)
			{
				reserve(n);
				for (size_t i = _size;i < n;++i)
					_str[i] = c;
				_str[_size = n] = '\0';
			}
		}
		void clear()
		{
			_str[_size = 0] = '\0';
		}
		bool empty() const
		{
			return _size == 0;
		}

		// String operations:
		const char* c_str()
		{
			return _str;
		}
		const char* data()
		{
			return _str;
		}
		size_t find(const string& str, size_t pos = 0) const
		{
			char* tmp = strstr(_str + pos, str._str);
			if (tmp == nullptr) // 查找子串失败
				return npos;
			return tmp - _str; // 返回两个指针之间的间隔距离,即表示找到的子串的起始位置
		}
		size_t find(char c, size_t pos = 0) const
		{
			for (size_t i = 0;i < _size;++i)
				if (_str[i] == c) return i;
			return npos;
		}
		size_t rfind(char c, size_t pos = 0) const
		{
			for (size_t i = _size - 1;i != npos;--i)
				if (_str[i] == c) return i;
			return npos;
		}
		string substr(size_t pos = 0, size_t len = npos) const
		{
			assert(pos < _size);
			string str;
			if (len == npos || pos + len >= _size)
				len = _size;
			for (size_t i = pos;i < len;++i)
				str += _str[i];
			return str;
		}

		// Element access:
		char& operator[](size_t n)
		{
			assert(n >= 0);
			assert(n < _size);
			return _str[n];
		}
		const char& operator[](size_t n) const
		{
			assert(n >= 0);
			assert(n < _size);
			return _str[n];
		}

		//Modifiers:
		void push_back(char c)
		{
			assert(_str);
			// 先判断是否需要扩容
			if (_size == _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 10 : 2 * _capacity;
				reserve(newcapacity);
			}
			_str[_size] = c;
			_str[++_size] = '\0'; // 增加字符以后不要忘记将_size增加
		}
		string& append(const string& str)
		{
			assert(_str && str._str);
			// 先判断是否需要扩容
			size_t len = strlen(str._str);
			if (_size + len >= _capacity)
			{
				size_t newcapacity = _size + len + 10; // 每次扩容都多增加是个容积的容错
				reserve(newcapacity);
			}
			memmove(_str + _size, str._str, len);
			_str[_size += len] = '\0';
			return *this;
		}
		string& operator+=(char c)
		{
			push_back(c);
			return *this;
		}
		string& operator+=(const string& str)
		{
			append(str);
			return *this;
		}
		string& insert(size_t pos, const string& str)
		{
			assert(pos <= _size);
			size_t len = strlen(str._str);
			if (_size + len >= _capacity)
				reserve(_size + len);
			size_t end = _size + len;
			while (end - len + 1 > pos)
			{
				_str[end] = _str[end - len];
				--end;
			}
			memmove(_str + pos, str._str, len);
			_size += len;
			return *this;
		}
		string& insert(size_t pos, char c)
		{
			assert(pos <= _size);
			if (_size >= _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 10 : 2 * _capacity;
				reserve(newcapacity);
			}
			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}
			_str[pos] = c;
			++_size;
			return *this;
		}
		string& erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);
			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t end = pos + len;
				while (end <= _size)
				{
					_str[end - len] = _str[end];
					++end;
				}
				_size -= len;
			}
			return *this;
		}
		void swap(string& str)
		{
			std::swap(_str, str._str);
			std::swap(_size, str._size);
			std::swap(_capacity, str._capacity);
		}

	private:
		char* _str = nullptr;
		size_t _size = 0;
		size_t _capacity = 0;
		
		// Member constants:
		const static size_t npos = -1;
	};

	bool operator==(const string& lhs, const string& rhs)
	{
		return (!(strcmp(lhs._str, rhs._str)) && lhs._size == rhs._size);
	}
	bool operator!=(const string& lhs, const string& rhs)
	{
		return !(lhs == rhs);
	}
	bool operator>(const string& lhs, const string& rhs)
	{
		return strcmp(lhs._str, rhs._str) > 0;
	}
	bool operator>=(const string& lhs, const string& rhs)
	{
		return (lhs > rhs || lhs == rhs);
	}
	bool operator<(const string& lhs, const string& rhs)
	{
		return !(lhs >= rhs);
	}
	bool operator<=(const string& lhs, const string& rhs)
	{
		return (lhs < rhs || lhs == rhs);
	}

	// 重载 流插入 & 流提取
	std::ostream& operator<<(std::ostream& out, const string& str)
	{
		for (auto& e : str)
			out << e;
		return out;
	}

	std::istream& operator>>(std::istream& in, string& str)
	{
		str.clear();
		char buffer[128] = {};
		int count = 0;
		for (char ch = in.get();ch != ' ' && ch != '\n';ch = in.get())
		{
			buffer[count++] = ch;
			if (count == 127)
			{
				str += buffer;
				count = 0;
			}
		}
		if (count > 0)
		{
			buffer[count] = '\0';
			str += buffer;
		}
		return in;
	}
	std::istream& getline(std::istream& in, string& str)
	{
		str.clear();
		char buffer[128] = {};
		int count = 0;
		for (char ch = in.get();ch != '\n';ch = in.get())
		{
			buffer[count++] = ch;
			if (count == 127)
			{
				str += buffer;
				count = 0;
			}
		}
		if (count > 0)
		{
			buffer[count] = '\0';
			str += buffer;
		}
		return in;
	}
}
  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Hyt的笔记本

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

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

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

打赏作者

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

抵扣说明:

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

余额充值