前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >【C++】深入探索string类的实现(下)(含源码)

【C++】深入探索string类的实现(下)(含源码)

作者头像
TANGLONG
发布于 2025-03-27 00:21:11
发布于 2025-03-27 00:21:11
4000
代码可运行
举报
运行总次数:0
代码可运行

该篇文章承接上一篇文章string类的实现(上),希望大家可以先看看上一篇文章,链接如下:【C++】深入探索string类的实现(上)

一、元素访问接口

    我们来看看访问接口有哪些,如下:

    这几个接口非常简单,前两个就是根据下标去取对应的字符,后面两个接口一个是取最后一个字符,一个是取第一个元素,这里我们直接给出实现了,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
char& operator[](size_t n)
{
	assert(n >= 0 && n < _size);
	return _str[n];
}

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

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

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

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

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

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

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

二、迭代器实现

    string类和我们之后要讲的vector的迭代器都非常好实现,因为它们两个容器的本质都是顺序表,底层都是数组,可以直接通过指针访问数据,所以string类的迭代器其实就是指针,只是被typedef了,而list等容器的迭代器则是一个类,我们以后会讲到,string类的迭代器如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
typedef char* iterator;
typedef const char* const_iterator;

    接下来我们来实现普通迭代器和const迭代器的begin和end,begin就是第一个元素的地址,end就是最后一个元素的下一个空间的地址,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
iterator begin()
{
	return _str;
}

iterator end()
{
	return _str + _size;
}

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

//const迭代器
const_iterator end() const
{
	return _str + _size;
}

    接下来我们使用范围for来测试一下迭代器,因为范围for的底层其实就是迭代器,如下:

    可以看到范围for可以跑通,说明我们的迭代器基本没问题,如果还有疑问可以自己尝试使用迭代器的方法测试

三、有关容量的接口

    我们来看看这些接口有哪些:

    可以看到其中有些接口是比较简单的,比如size、capacity、empty、clear等等,这些我们在顺序表那里就学过了,只是其中这个clear没有学过,但是也非常简单,清空数据可以直接把size置为0,然后补上\0就可以了,上面这些函数的代码我们就直接写出来了,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
size_t size() const
{
	return _size;
}

size_t capacity() const
{
	return _capacity;
}

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

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

    这里我们只需要实现resize接口就可以了,因为扩容接口reserve我们已经实现过了,我们来看看它的所有接口:

    这两个接口如果n比size大都会填充字符,只是第一个字符填充\0,而第二个接口需要指定填充的字符,所以我们其实可以把这两个接口写成一个接口,其中字符c的默认值为\0就可以了,接下来我们来分析如何实现resize

    resize有三种情况我们就用if将这三种情况进行划分,如果n小于或等于原本的size,那么我们就直接将size改成n就行了,进行对应的缩容,但是不要忘了把\0添加到最后,如果n大于size但是小于等于当前的容量,那么我们就把中间的空间都填充成对应的字符,如果n大于当前容量就进行扩容,并且从size到n都填充对应的字符,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void string::resize(size_t n, char c)
{
	assert(n >= 0);
	if (n <= _size)
	{
		_size = n;
		_str[_size] = '\0';
	}
	else if (n <= _capacity)
	{
		//走到这里n一定大于size,但是小于等于capacity
		for (size_t i = _size; i < n; i++)
		{
			_str[i] = c;
		}
		_size = n;
		_str[_size] = '\0';
	}
	else
	{
		//n大于capacity,扩容
		reserve(n);
		for (size_t i = _size; i < n; i++)
		{
			_str[i] = c;
		}
		_str[_size] = '\0';
	}
}

    上面就是resize的实现,但是我们发现有两个地方代码有一些冗余,一个地方是三个判断中我们都要将size位置的数据改为\0,我们可以把它拿出来减少冗余

    第二个地方就是第二个和第三个判断中挪动数据的逻辑一致,只是有一个没有用reserve,但是我们想想我们之前实现的reserve是不是在扩容前会进行一次判断,如果参数小于capacity就不会扩容,所以第二个判断中n大于size但是小于capacity,就算使用reserve也不会发生扩容,因为在reserve函数中作了判断,所以我们可以将第二个判断和第三个判断合并,优化后如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void string::resize(size_t n, char c)
{
	assert(n >= 0);
	if (n <= _size)
	{
		_size = n;
	}
	else
	{
		//如果n小于capacity不会发生扩容
		reserve(n);
		for (size_t i = _size; i < n; i++)
		{
			_str[i] = c;
		}
		_size = n;
	}
	//都要执行这个逻辑就拿出来
	_str[_size] = '\0';
}

四、字符串操作

    我们来看看有哪些函数:

c_str

    首先第一个c_str最简单,直接返回string对象底层的数组即可,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const char* c_str() const
{
	return _str;
}

find

    接下来我们来实现两个查找方法,首先是正向的find,我们来看看具体有哪些接口:

    这里我们实现两个接口,一个是查找一个字符,一个是查找string类对象,首先我们来看查找一个字符,只需要我们从pos位置开始遍历容器即可,如果查找到就返回下标,否则返回npos,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
size_t string::find(char c, size_t pos)
{
    //如果pos过大进入不了循环
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == c)
		{
			return i;
		}
	}
	return npos;
}

    接下来我们就来实现查找一个string对象,它的本质其实是查找这个string对象中的字符串,我们可以通过函数strstr实现,具体方法就是在遍历过程中看第一个字符是否相同,如果相同那么就通过strstr函数进行匹配,成功了就返回,失败继续查找,到最后都没有找到就返回npos,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
size_t string::find(const string& str, size_t pos)
{
	//如果pos过大进入不了循环
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == str[i])
		{
			char* ret = strstr(_str, str._str);
			if (ret)
			{
				return i;
			}
		}
	}
	return npos;
}

rfind

    rfind的接口和find的接口也差不多,如下:

    rfind我们也实现两个接口,一个是查找字符,一个是查找string对象,其实rfind和find的实现基本上没什么不同,只是rfind从后往前找而已,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
size_t string::rfind(char c, size_t pos)
{
	if (pos > _size)
		pos = _size - 1;
	for (size_t i = pos; i >= 0; i--)
	{
		if (_str[i] == c)
		{
			return i;
		}
	}
	return npos;
}

size_t string::rfind(const string& str, size_t pos)
{
	if (pos > _size)
		pos = _size - 1;
	for (size_t i = pos; i >= 0; i--)
	{
		if (_str[i] == str[0])
		{
			char* ret = strstr(_str, str._str);
			if (ret)
			{
				return i;
			}
		}
	}
	return npos;
}

substr

    substr的接口形式只有一个,就是从pos位置开始,向后截取长度为len的字符串,最后以string的形式返回,如下:

    这里我们要注意的是在截取字符串的时候是否越界,比如当len > _size + pos,这个时候就会发生越界,因为_size - pos表示当前字符串还剩多少个字符可以被截取,如果len大于了这个数量就会照成越界,此时只需让len = _size + pos即可

    解决了越界的问题我们就开始截取字符串,方法也很简单,我们用循环的方式把对应的字符插入一个新的string类对象中即可,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
string string::substr(size_t pos, size_t len)
{
	//pos不会小于0,因为类型为size_t
	assert(pos < _size);
	//_size - pos表示剩余的字符数,len不能大于这个数
	if (len > _size - pos)
		len = _size - pos;
	string str;
	str.reserve(len);
	for (size_t i = pos; i < pos + len; i++)
	{
		str += _str[i];
	}
	return str;
}

compare

    我们来看看compare函数的接口,如下:

    compare的底层就是封装了strcmp,我们就实现图上的第一个接口就行了,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int string::compare(const string& str) const
{
	return strcmp(_str, str._str);
}

五、非成员函数重载

各种关系运算重载

    关系运算就是等于、大于、小于等运算,接下来我们来实现一下这些关系运算,只需要利用之前写的compare函数即可,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool operator==(const string& str1, const string& str2)
{
	return str1.compare(str2) == 0;
}

bool operator!=(const string& str1, const string& str2)
{
	return !(str1 == str2);
}

bool operator<(const string& str1, const string& str2)
{
	return str1.compare(str2) < 0;
}

bool operator<=(const string& str1, const string& str2)
{
	return str1 < str2 || str1 == str2;
}

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

bool operator>=(const string& str1, const string& str2)
{
	return str1 > str2 || str1 == str2;
}

流提取运算符>>重载

    流提取运算符的重载是为了方便我们输出string类对象,方法也很简单,就是把string对象的字符一个一个输出即可,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ostream& operator<<(ostream& out, const string& str)
{
	for (int i = 0; i < str.size(); i++)
	{
		cout << str[i];
	}
	return out;
}

流插入运算符<<重载

    流插入运算符重载是为了方便我们给string对象手动进行输入,写这个函数要注意的地方还是挺多的,第一个注意的点是我们去读取字符的时候不能直接用cin,因为cin不会读取空格和\n,要想读取所有类型的字符就要用istream对象的成员函数get,等下我们会演示

    第二个点是我们在进行插入之前要把以前的数据清空,否则就达不到我们的预期,其实还有第三个点,但是我们可以后面说,我们先按照上面的大致思路写出代码,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
istream& operator>>(istream& in, string& str)
{
	//清空之前的数据
	str.clear();
	char c = in.get();
	while (c != ' ' && c != '\n')
	{
		str += c;
		c = in.get();
	}
	return in;
}

    上面的代码其实已经可以运行了,但是其实还是有一些缺点,就是效率可能不是很高,因为如果我们输入的字符串很长,那么就会反复进行扩容,所以为了提高效率我们可以设计一个缓冲区,先将数据放入这个缓冲区,等缓冲区满了再一次性写入对象,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
istream& operator>>(istream& in, string& str)
{
	str.clear();
	char c = in.get();
	int i = 0;
	char buff[256];
	while (c != ' ' && c != '\n')
	{
		buff[i++] = c;
		c = in.get();
		if (i == 255)
		{
			buff[i] = '\0';
			str += buff;
			i = 0;
		}
	}
	if (i > 0)
	{
		buff[i] = '\0';
		str += buff;
	}
	return in;
}

getline实现

    getline和流插入运算符重载其实很像,只是它们循环的判断条件不同,getline是根据分隔符delim来判断循环是否继续,delim的默认值是\n,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
istream& getline(istream& in, string& str, char delim)
{
	str.clear();
	char c = in.get();
	int i = 0;
	char buff[256];
	while (c != delim)
	{
		buff[i++] = c;
		c = in.get();
		if (i == 255)
		{
			buff[i] = '\0';
			str += buff;
			i = 0;
		}
	}
	if (i > 0)
	{
		buff[i] = '\0';
		str += buff;
	}
	return in;
}

六、源码

string.h

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#pragma once
#include <iostream>
#include <cstring>
#include <cassert>
using namespace std;

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

		//构造
		string(const char* s = "");
		string(size_t n, char c);
		//现代写法需要用到swap
		void swap(string& str);
		//拷贝构造与赋值重载
		string(const string& str);
		string& operator=(const string& str);
		//析构
		~string();

		void reserve(size_t n);
		void push_back(const char c);
		void pop_back();
		void append(const string& str);
		void append(size_t n, char c);
		void insert(size_t pos, const string& str);
		void insert(size_t pos, size_t n, char c);
		void erase(size_t pos = 0, size_t len = npos);
		string& operator+=(const string& str);
		string& operator+=(char c);
		void resize(size_t n, char c = '\0');
		size_t find(char c, size_t pos = 0);
		size_t find(const string& str, size_t pos = 0);
		size_t rfind(char c, size_t pos = npos);
		size_t rfind(const string& str, size_t pos = npos);
		string substr(size_t pos = 0, size_t len = npos);
		int compare(const string& str) const;

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

		char& at(size_t n)
		{
			assert(n < _size);
			return _str[n];
		}

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

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

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

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

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

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

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

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

		//const迭代器
		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()
		{
			_size = 0;
			_str[_size] = '\0';
		}


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

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

	string operator+(char c, const string& str);
	string operator+(const char* s, const string& str);
	string operator+(const string& str, char c);
	string operator+(const string& str, const char* s);
	void swap(string& s1, string& s2);
	istream& getline(istream& in, string& str, char delim = '\n');
	ostream& operator<<(ostream& out, const string& str);
	istream& operator>>(istream& in, string& str);
	bool operator==(const string& str1, const string& str2);
	bool operator!=(const string& str1, const string& str2);
	bool operator<(const string& str1, const string& str2);
	bool operator<=(const string& str1, const string& str2);
	bool operator>(const string& str1, const string& str2);
	bool operator>=(const string& str1, const string& str2);

}

string.cpp

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define _CRT_SECURE_NO_WARNINGS

#include "string.h"

namespace TL
{
	const size_t string::npos = -1;

	string::string(const char* s)
		:_size(strlen(s))
	{
		_capacity = _size;
		//注意要多开一个空间存放\0
		_str = new char[_capacity + 1];
		//strcpy会拷贝\0
		strcpy(_str, s);
	}

	string::string(size_t n, char c)
		:_size(n)
	{
		_capacity = _size;
		//注意要多开一个空间存放\0
		_str = new char[_capacity + 1];
		for (size_t i = 0; i < _size; i++)
		{
			_str[i] = c;
		}
		_str[_size] = '\0';
	}

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


	string::string(const string& str)
	{
		//以前的写法
		/*if (_str)
			delete[] _str;
		_str = new char[str._capacity + 1];
		_size = str._size;
		_capacity = str._capacity;
		strcpy(_str, str._str);*/

		//现代写法(利用构造)
		string tmp(str._str);
		swap(tmp);
	}

	string& string::operator=(const string& str)
	{
		//注意判断,不要自己给自己赋值
		if (this != &str)
		{
			//普通写法
			/*if (_str)
				delete[] _str;
			_str = new char[str._capacity + 1];
			strcpy(_str, str._str);
			_size = str._size;
			_capacity = str._capacity;*/

			//利用拷贝构造,也可以利用构造
			string tmp(str);
			swap(tmp);
		}
		return *this;
	}

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

	void string::reserve(size_t n)
	{
		//如果参数小于等于容量就不予处理
		if (n <= _capacity)
		{
			return;
		}
		//2倍扩容
		size_t newCapacity = _capacity * 2;
		//如果2倍扩容不够就直接扩容到n
		if (newCapacity < n)
			newCapacity = n;
		//开新空间,注意还是要多开一个空间以后放\0用
		char* tmp = new char[newCapacity + 1];
		//数据的拷贝
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capacity = newCapacity;
	}


	void string::push_back(char c)
	{
		//注意查看空间是否满了,如果满了就扩容
		if (_size == _capacity)
		{
			reserve(2 * _capacity);
		}
		_str[_size++] = c;
		_str[_size] = '\0';
	}

	void string::pop_back()
	{
		//确保数组不为空
		assert(_size > 0);
		//size--相当于就把它删除了
		_size--;
		//补上\0
		_str[_size] = '\0';
	}


	void string::append(const string& str)
	{
		size_t len = strlen(str._str);
		if (len + _size > _capacity)
		{
			reserve(len + _size);
		}
		//将str的字符串拷贝到当前对象
		//注意\0会跟着一起拷贝,不需要专门给
		strcpy(_str + _size, str._str);
		_size += len;
	}

	void string::append(size_t n, char c)
	{
		if (n + _size > _capacity)
		{
			reserve(n + _size);
		}
		for (size_t i = _size; i < n + _size; i++)
		{
			_str[i] = c;
		}
		_size += n;
		_str[_size] = '\0';
	}

	void 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 src = _size - 1;
		size_t dest = src + len;
		//这里如果不强转为int就会导致死循环和越界访问
		//因为src类型是size_t,pos为0再--会变成非常大的数
		while ((int)src >= (int)pos)
		{
			_str[dest--] = _str[src--];
		}
		//下面这句strcpy也能实现上面的拷贝功能
		//strcpy(_str + pos + len, _str + pos);
		/*dest = pos;
		for (size_t i = 0; i < str._size; i++)
		{
			_str[dest++] = str._str[i];
		}*/
		//不拷贝\0
		strncpy(_str + pos, str._str, strlen(str._str));
		_size += len;
		_str[_size] = '\0';
	}

	void string::insert(size_t pos, size_t n, char c)
	{
		assert(pos <= _size);
		if (_size + n > _capacity)
		{
			reserve(_size + n);
		}
		size_t src = _size - 1;
		size_t dest = src + n;
		while ((int)src >= (int)pos)
		{
			_str[dest--] = _str[src--];
		}
		//循环填充字符
		for (size_t i = pos; i < pos + n; i++)
		{
			_str[i] = c;
		}
		_size += n;
		_str[_size] = '\0';
	}

	void string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);
		if (len > _size - pos)
			len = _size - pos; 
		size_t src = pos + len;
 		size_t dest = pos;
		/*for (size_t i = pos + len; i < _size; i++)
		{
			_str[dest++] = _str[src++];
		}*/
		while (src < _size)
		{
			_str[dest++] = _str[src++];
		}
		//下面这句strcpy也可以实现数据的覆盖拷贝,达到删除的目的
		//strcpy(_str + pos, _str + pos + len);
		_size -= len;
		_str[_size] = '\0';
	}

	//void string::resize(size_t n, char c)
	//{
	//	assert(n >= 0);
	//	if (n <= _size)
	//	{
	//		_size = n;
	//		_str[_size] = '\0';
	//	}
	//	else if (n <= _capacity)
	//	{
	//		//走到这里n一定大于size,但是小于等于capacity
	//		for (size_t i = _size; i < n; i++)
	//		{
	//			_str[i] = c;
	//		}
	//		_size = n;
	//		_str[_size] = '\0';
	//	}
	//	else
	//	{
	//		//n大于capacity,扩容
	//		reserve(n);
	//		for (size_t i = _size; i < n; i++)
	//		{
	//			_str[i] = c;
	//		}
	//		_str[_size] = '\0';
	//	}
	//}

	void string::resize(size_t n, char c)
	{
		if (n <= _size)
		{
			_size = n;
		}
		else
		{
			//如果n小于capacity不会发生扩容
			reserve(n);
			for (size_t i = _size; i < n; i++)
			{
				_str[i] = c;
			}
			_size = n;
		}
		//都要执行这个逻辑就拿出来
		_str[_size] = '\0';
	}

	

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

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

	//不是成员函数
	string operator+(char c, const string& str)
	{
		//之前实现的n个c字符的构造
		string tmp(1, c);
		//直接复用+=
		return tmp += str;
	}

	string operator+(const char* s, const string& str)
	{
		//字符串的构造
		string tmp(s);
		//复用+=
		return tmp += str;
	}

	string operator+(const string& str, char c)
	{
		string tmp(str);
		//利用匿名对象
		return tmp += string(1, c);
	}

	string operator+(const string& str, const char* s)
	{
		string tmp(str);
		//利用隐式类型转换,将s转换为string然后+=
		return tmp += s;
	}

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

	size_t string::find(char c, size_t pos)
	{
		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == c)
			{
				return i;
			}
		}
		return npos;
	}

	size_t string::find(const string& str, size_t pos)
	{
		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == str[i])
			{
				char* ret = strstr(_str, str._str);
				if (ret)
				{
					return i;
				}
			}
		}
		return npos;
	}

	size_t string::rfind(char c, size_t pos)
	{
		if (pos > _size)
			pos = _size - 1;
		for (size_t i = pos; i >= 0; i--)
		{
			if (_str[i] == c)
			{
				return i;
			}
		}
		return npos;
	}

	size_t string::rfind(const string& str, size_t pos)
	{
		if (pos > _size)
			pos = _size - 1;
		for (size_t i = pos; i >= 0; i--)
		{
			if (_str[i] == str[0])
			{
				char* ret = strstr(_str, str._str);
				if (ret)
				{
					return i;
				}
			}
		}
		return npos;
	}

	string string::substr(size_t pos, size_t len)
	{
		//pos不会小于0,因为类型为size_t
		assert(pos < _size);
		//_size - pos表示剩余的字符数,len不能大于这个数
		if (len > _size - pos)
			len = _size - pos;
		string str;
		str.reserve(len);
		for (size_t i = pos; i < pos + len; i++)
		{
			str += _str[i];
		}
		return str;
	}

	int string::compare(const string& str) const
	{
		return strcmp(_str, str._str);
	}

	istream& getline(istream& in, string& str, char delim)
	{
		str.clear();
		char c = in.get();
		int i = 0;
		char buff[256];
		while (c != delim)
		{
			buff[i++] = c;
			c = in.get();
			if (i == 255)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
		}
		if (i > 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return in;
	}

	ostream& operator<<(ostream& out, const string& str)
	{
		for (int i = 0; i < str.size(); i++)
		{
			cout << str[i];
		}
		return out;
	}

	istream& operator>>(istream& in, string& str)
	{
		str.clear();
		char c = in.get();
		int i = 0;
		char buff[256];
		while (c != ' ' && c != '\n')
		{
			buff[i++] = c;
			c = in.get();
			if (i == 255)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}
		}
		if (i > 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return in;
	}

	bool operator==(const string& str1, const string& str2)
	{
		return str1.compare(str2) == 0;
	}

	bool operator!=(const string& str1, const string& str2)
	{
		return !(str1 == str2);
	}

	bool operator<(const string& str1, const string& str2)
	{
		return str1.compare(str2) < 0;
	}

	bool operator<=(const string& str1, const string& str2)
	{
		return str1 < str2 || str1 == str2;
	}

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

	bool operator>=(const string& str1, const string& str2)
	{
		return str1 > str2 || str1 == str2;
	}

}

    那么到这里string类的实现我们就讲完了,是不是对string有了新的理解呢?下一篇文章我们就开始学习vector了,敬请期待吧!     bye~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-03-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【C++】string类的模拟实现
分为无参和带参这两种构造函数。无参构造函数默认构造空字符串"",所以我们只需要给一个缺省值即可。
平凡的人1
2022/11/21
7380
【C++】string类的模拟实现
【C++】string类——模拟实现
通过模拟实现string类的主要接口可以使我们对string类的理解更加透彻,深入理解内存管理,可以更好地理解字符串在内存中的存储方式,以及如何进行内存分配和释放,从而避免常见的内存泄漏和溢出问题,加深对面向对象编程理念的理解,比如封装、继承和多态。
_小羊_
2024/10/16
1160
【C++】STL--string(下)
1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差 不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可 以连接字符串。
用户11375356
2024/11/22
490
【C++】STL--string(下)
【C++】string类模拟实现
在之前的两篇博客中已经分享关于string类的使用,有需要可以点击链接看看【C++】string类初步介绍和链接: 【C++】string进一步介绍,这次要分享用C++代码来实现string类。
zxctscl
2024/03/23
1640
【C++】string类模拟实现
【c++】string类模拟实现
我们stl库中的string类实在std命名空间的,这里我们自定义一个命名空间own,包含string类和简单的成员变量:
用户11029103
2024/04/20
830
【c++】string类模拟实现
【C++】string类模拟实现:探索其内部机制
通过对string类的学习,我们知道string类的模拟实现最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数,以下是对模拟实现string类变量以及函数的封装:
大耳朵土土垚
2024/05/24
1230
【C++】string类模拟实现:探索其内部机制
[C++]string及其模拟实现
string及其模拟实现:: 1.构造函数 //注意: '\0' "\0" ""的区别 string(const char* str = "") { //对空指针不能strlen _size = strlen(str); _capacity = _size; _str = new char[_capacity + 1]; strcpy(_str, str); } 2.拷贝构造函数 传统写法: //拷贝构造传统写法 //s2(s1) string(const string& s) { _
IT编程爱好者
2023/05/11
2750
string类(下)(模拟实现string类,深度剖析其底层)
断言(Assertion)是编程中一种常用的调试辅助手段,用于在代码执行期间验证某个条件是否为真。如果条件为真(即满足预期),则程序继续执行;如果条件为假(即不满足预期),则断言失败,通常会导致程序抛出一个错误、输出一条错误信息,甚至直接终止程序。断言主要用于开发和测试阶段,以确保代码的正确性和健壮性。
suye
2024/10/16
960
【C++】string类(下)
构造函数new的地方开多一个空间放’\0’,因为是字符串,由于初始化时的顺序是按照声明的顺序,在初始化列表初始化指针_str会因为容量无数据导致随机值,所以我们不用初始化列表初始化指针_str,等它们通过初始化列表初始化完成后再赋初值
s-little-monster
2024/08/09
850
【C++】string类(下)
string类的模拟实现
上一篇博客我们对string类函数进行了讲解,今天我们就对string类进行模拟实现,以便于大家更加深入地了解string类函数的应用
ahao
2024/03/19
1160
string类的模拟实现
【C++】string学习 — 手搓string类项目
C++ 的 string 类是 C++ 标准库中提供的一个用于处理字符串的类。它在 C++ 的历史中扮演了重要的角色,为字符串处理提供了更加方便、高效的方法。
叫我龙翔
2024/03/13
1760
【C++】string学习 — 手搓string类项目
【C++】string类
string类的文档介绍 https://cplusplus.com/reference/string/string/?kw=string
啊QQQQQ
2024/11/19
570
【C++】string类
【C++】string类的模拟实现
​ 该函数的作用:在 pos 位置上插入 字符c 或者 字符串str ,并返回该字符的位置!
利刃大大
2025/02/07
540
【C++】STL---string
C语言中,字符串是以 ‘\0’ 结尾的一些字符的集合,为了操作方便,C标准库中提供了一些 str 系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
YoungMLet
2024/03/01
1680
【C++】STL---string
【C++】深度解析:用 C++ 模拟实现 string 类,探索其底层实现细节
2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
P_M_P
2024/05/26
1670
【C++】深度解析:用 C++ 模拟实现 string 类,探索其底层实现细节
【C++修炼之路】9. string类的模拟实现
本篇文章是衔接上一篇string,进行string的模拟实现,其中包含了众多重载函数,以及一些实现的细节,由于上篇已经知道具体函数的含义,这一篇就以纯代码的方式进行叙述。此外,这篇还对内置类型的知识进行了进一步的扩展。
每天都要进步呀
2023/03/28
2730
【C++修炼之路】9. string类的模拟实现
探索C/C++的奥秘之string类(二)
operator[]有两个版本,意义是不一样的,普通对象调用第一个,能读能写,const对象调用第二个,只能读。
用户11290648
2024/11/21
570
探索C/C++的奥秘之string类(二)
【C++】—— string模拟实现
默认构造函数就是不需要传参的构造函数;这里实现就开辟一个字符的空间存放 '\0'即可(_capacity不包括 '\0' )。
星辰与你
2024/10/17
460
【C++】—— string模拟实现
C++STL——string类与模拟实现
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。 STL的六大组成:仿函数,算法,迭代器,空间配置器,容器,配接器。 注意: 这里我是按照功能归类讲string归类到了STL里面,如果按照发展史其实并不属于STL中的容器。 并且从现在开始我们会更频繁的开始使用这个网站了: cpulspuls
有礼貌的灰绅士
2023/03/28
2850
C++STL——string类与模拟实现
C/C++:string类的模拟实现
string的文档网站 string类的介绍以及一些常见问题 String是一个管理字符数组的类,要求这个字符数组结尾用 ‘\0’ 标识 涉及的问题如下: 拷贝构造和赋值重载实现 深拷贝 增删查改的相关接口 重载一些常见的运算符如:[] 、>> 、<< 等 迭代器 对于一个成员函数,什么时候该加const呢? 1 、如果是 只读函数 ,则要加 const 2 、如果是 只写函数 ,则不能加 const 3 、如果 既是可读又是可写的函数 ,则要重载两个版本的函数,即 const 版本
利刃大大
2023/04/12
3700
相关推荐
【C++】string类的模拟实现
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文