前往小程序,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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
新GEO
1.Entrez gene ID:我们一般说的Gnen ID即Entrez gene ID,是用一串数字表示的(在NCBI里面用)
用户10758803
2024/03/10
2330
GEO数据分析流程之芯片4
生信菜鸟团
2024/06/28
930
GEO数据分析流程之芯片4
R语言可视化STRING分析的蛋白互作网络(PPI)
STRING 链接 https://string-db.org/ 数据集我使用R语言包clusterProfiler中经常用作示例的基因列表 获取gene symbol的代码
用户7010445
2020/03/05
4.9K3
R语言可视化STRING分析的蛋白互作网络(PPI)
WGCNA-最短教程掌握高级分析
先给大家讲讲WGCNA的精髓,其实就一句话:关联表型和基因。WGCNA通过将基因进行分组(module),把基因模块和表型进行关联,实现了快速锁定核心基因的目的。
用户6317549
2019/12/30
3.9K0
从网络图探寻基因互作的蛛丝马迹(5)
在前面的4期中,我们分别给大家讲解了网络图的构造、 STRING 数据库、Cytoscape 软件的安装以及使用,链接如下:
用户6317549
2019/09/24
1.6K0
从网络图探寻基因互作的蛛丝马迹(5)
cytoscape的cytohubba及MCODE插件寻找子网络hub基因
不过,好在我有一千多学员,一百多个学徒,给他们安排的作业就是写这些简单软件操作指南,这样就弥补了我写不来太基础教程的弱点。
生信技能树
2020/02/20
23.8K1
​cytoscape的十大插件之二--MCODE插件
Identification of differentially expressed and methylated genes associated with rheumatoid arthritis based on network: Autoimmunity: Vol 53, No 6 (tandfonline.com)
生信技能树
2021/04/29
20.1K1
​cytoscape的十大插件之二--MCODE插件
寻找核心基因+子网络
一般做完差异基因,或者使用其他方法找到想要的biomarker时,想要知道这些基因的调控网络,或者哪些基因在调控网络中处于核心位置,比较常见的方法就是wgcna或者mcode、Cytohubba。这篇主要介绍mcode和Cytohubba。
用户1359560
2020/05/04
2.7K0
STRING网站+Cytoscape软件制作精美蛋白互作网络图(PPI)
之前小编为大家推送了利用DAVID网站进行差异基因的GO和KEGG分析,而基因功能注释后就可以寻找蛋白表达之间的关系了,在生信分析中,常常会使用STRING网站+Cytoscape软件来制作蛋白互作网络图(PPI)。今天小编奉上一部PPI制作教程,让我们一起细细咀嚼吧!
百味科研芝士
2019/05/29
49.4K2
​cytoscape的十大插件之--cytoHubba插件
cytoscape 毋庸置疑是最出名的网络可视化神器,过万的引用率是最强大的口碑,它支持的网络种类很多。比如蛋白互作(PPI)、转录调控网络图(TF-target)、网络聚类模块分析(Module)、miRNA调控靶标基因网络图、竞争性内源RNA网络(ceRNA)、通路交互网络(pathway-crosstalk)。Download Cytoscape
生信技能树
2021/04/29
23.1K0
​cytoscape的十大插件之--cytoHubba插件
RNA-seq入门实战(十):PPI蛋白互作网络构建(下)——Cytoscape软件的使用
连续两次求贤令:曾经我给你带来了十万用户,但现在祝你倒闭,以及 生信技能树知识整理实习生招募,让我走大运结识了几位优秀小伙伴!大家开始根据我的ngs组学视频进行一系列公共数据集分析实战,其中几个小伙伴让我非常惊喜,不需要怎么沟通和指导,就默默的完成了一个实战!
生信技能树
2022/07/26
4.1K0
RNA-seq入门实战(十):PPI蛋白互作网络构建(下)——Cytoscape软件的使用
生信必备技能——Cytoscape
就应该会纳闷,为什么拿到了差异基因并且注释后就结束了,明明大量的数据挖掘文章都有一个网络图并且找hub基因啊!
生信技能树
2021/04/29
3.2K0
生信必备技能——Cytoscape
RNA-seq入门实战(九):PPI蛋白互作网络构建(上)——STRING数据库的使用
连续两次求贤令:曾经我给你带来了十万用户,但现在祝你倒闭,以及 生信技能树知识整理实习生招募,让我走大运结识了几位优秀小伙伴!大家开始根据我的ngs组学视频进行一系列公共数据集分析实战,其中几个小伙伴让我非常惊喜,不需要怎么沟通和指导,就默默的完成了一个实战!
生信技能树
2022/07/26
6.1K0
RNA-seq入门实战(九):PPI蛋白互作网络构建(上)——STRING数据库的使用
从网络图探寻基因互作的蛛丝马迹(4)
在前面的3期中,我们给大家讲解了网络图的构造、 STRING数据库和Cytoscape软件的安装,链接如下:
用户6317549
2019/09/24
9890
从网络图探寻基因互作的蛛丝马迹(4)
Cytoscape: MCODE增强包的网络模块化分析
之前的教程提供了Cytoscape基础和视频、R igraph包的网络构建方法,那么在我们得到network图之后,还可以进行深一步分析,今天给大家带来基于Cytoscape软件下MCODE增强包的模块化分析。
生信宝典
2018/12/24
5.6K0
Cytoscape: MCODE增强包的网络模块化分析
R语言学习笔记-Day10
R^2越大,越接近无标度网络,选择使R^2第一次到达0.8/0.85/0.9的β值
用户11190095
2024/07/17
1170
生信宝典之傻瓜式(四)蛋白蛋白互作网络在线搜索
傻瓜系列重启了,今天要介绍的是一款在线查询蛋白-蛋白互作网络的工具 STRING (https://string-db.org/)。 STRING数据库收录了2031个物种,9.6 Million个蛋
生信宝典
2018/02/05
2.7K0
生信宝典之傻瓜式(四)蛋白蛋白互作网络在线搜索
GEO数据库中芯片数据分析思路
AnnoProbe是曾建明老师2020年开发的一款用于下载GEO数据集并注释的R包,收录在tinyarray里。 idmap##根据所给的GPL号,返回探针的注释 geoChina##根据所给的GSE号,下载对应的表达矩阵 annoGene##根据gencode中的GTF文件注释基因ID
小张小张
2023/05/25
1.9K0
GEO数据分析前准备
生信菜鸟团
2024/06/28
1590
GEO数据分析前准备
纯生信公共数据分析怎样才能发到11分+?
癌旁组织在肝细胞癌(HCC)患者中的预后作用仍不清楚。癌旁与癌症组织发挥作用的基因集可能存在差异。
作图丫
2022/03/29
3300
纯生信公共数据分析怎样才能发到11分+?
推荐阅读
相关推荐
新GEO
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文