手撕string类(部分)

一、头文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<assert.h>//断言库
#include<iostream>
#include<string>
using namespace std;

namespace wxt//写在自己的命名空间中
{
	class string
	{
	public:
		string(const char* str = "");//带参构造,给缺省处理空字符串的情况
		~string();//析构
		size_t size() const;//返回对象大小
		size_t capacity() const;//对象容量
		char& operator[](size_t pos);//对下标引用操作符的重载
		const char operator[](size_t pos) const;//下标引用操作符的const重载
		typedef char* iterator;//迭代器
		iterator begin();//开始迭代器
		iterator end();//结束迭代器
		void reserve(size_t n);//对象扩容函数
		void push_back(char ch);//尾插字符函数
		void append(const char* str);//尾插字符串函数
		string& operator+=(char ch);//类似尾插字符
		string& operator+=(const char* str);//类似尾插字符串
		void insert(size_t pos, char ch);//任意位置插入字符
		bool operator<(const string& s) const;//符号重载
		bool operator==(const string& s) const;
		bool operator<=(const string& s) const;
		bool operator>(const string& s) const;
		bool operator>=(const string& s) const;
		bool operator!=(const string& s) const;
		void clear();//情况对象数据
		const char* str() const;//以字符串返回对象
		typedef char* iterator;//针对const对象的迭代器
		iterator begin() const;
		iterator end() const;
//——————————————————————————————
//——————————————————————————————
		void insert(size_t pos, const char* str);//任意插入字符串
		void erase(size_t pos, size_t len = std::string::npos);//删除字符或字符串
		void rsize(size_t n, char ch = '\0');//设置对象大小
		size_t find(char ch, size_t pos = 0) const;//查找对象中指定字符
		size_t find(const char* str, size_t pos = 0) const;//查找对象中指定字符串
		string substr(size_t pos = 0, size_t len = std::string::npos) const;//截取对象部分元素并作为一个新的元素
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
	ostream& operator<<(ostream& out, const string& s);//流输出重载
	istream& operator>>(istream& in, string& s);//流提取重载
}

二、函数具体实现解析:

2.1构造函数:

  • 可以有参也可以无参
  • 声明给缺省值,定义不给缺省值。使用一个不包含任何数据的双引号作为缺省值,是因为双引号可以表示一个字符串,自动包含一个'\0'。就可以不用写无参构造。
  • 初始化列表完成对大小和容量的初始化,函数体内完成空间的开辟和内容的拷贝。
//声明
string(const char* str = "");//带参构造,给缺省处理空字符串的情况

//定义
wxt::string::string(const char* str)//给一个缺省值,处理传空字符串的情况
	:_size(strlen(str))//大小开辟为传来的字符串的长度
	, _capacity(_size)//容量设置为字符串的大小
{
	_str = new char[_capacity + 1];//多开辟一个字节空间,存放\0
	strcpy(_str, str);//将空字符串拷贝过去
}

2.2析构函数

  • 主要完成释放空间,指针置空,大小和容量归零。
//声明
~string();


//定义
wxt::string::~string()
{
	delete[] _str;//释放对象在堆上动态开辟的空间
	_str = nullptr;//将指向堆上空间的指针置空
	_size = _capacity = 0;//将大小和容量置空
}

2.3返回对象的大小和容量:

  • 加上const修饰this指针,使得值不可被修改。
//声明
size_t size() const;
size_t capacity() const;


//定义
size_t wxt::string::size() const//返回对象大小,加const修饰*this不被修改
{
	return _size;
}

size_t wxt::string::capacity() const//返回容量大小,加const成员修饰this指针
{
	return _capacity;
}

2.4下标引用操作符的重载:

  • 作用是可以像操作字符串一样操作string对象
  • 使用引用返回,就可以间接修改对象指定下标处的元素。
  • 加上断言,处理错误传递下标的情况
//声明
char& operator[](size_t pos);



//定义
char& wxt::string::operator[](size_t pos)//传引用返回,可以修改指定下标处字符
{
	assert(pos < _size);//断言下标为合法位置
	return _str[pos];//返回对象下标
}

2.5下标引用操作符的const版本重载:

  • const版本主要用于下标操作const修饰的string对象
  • 由于和非const版本的参数完全相同,需要加上const成员修饰this指针构成函数重载。
//声明
const char operator[](size_t pos) const;



//定义
const char wxt::string::operator[](size_t pos) const
//前面的const修饰函数,表示该函数为const修饰的string对象使用
//后面的const修饰this指针,与上一个[]的运算符重载构成函数重载
{
	assert(pos < _size);
	return _str[pos];
}

2.6迭代器:

  • 迭代器实际返回的是一个指针,所以将char*类型的指针,重命名为iterator
  • begin返回_str即可,代表首元素的地址
//声明
typedef char* iterator;
iterator begin();
iterator end();


//定义
wxt::string::iterator wxt::string::begin()
{
	return _str;//返回字符串名字,相当于首元素地址
}

wxt::string::iterator wxt::string::end()
{
	return _str + _size;//返回最后一个字符后一个的地址
						//相当于返回\0的地址,原string对象end-1才是最后一个字符地址
}

2.7针对const对象的迭代器:

  • 思路同上,加上const成员修饰this指针,使得构成函数重载
//声明
typedef char* iterator;
iterator begin() const;
iterator end() const;


//定义
wxt::string::iterator wxt::string::begin() const
{
	return _str;
}
wxt::string::iterator wxt::string::end() const
{
	return _str + _size;
}

2.8预留空间函数:

  • 如果要求预留的空间大于对象当前容量才进行扩容,否则不进行扩容。
  • 思路就是数据的迁移,先开要求大小的新空间,将旧空间的数据迁移过去,释放旧空间,对象中指向旧空间的指针指向新空间,更新对象的容量。
//声明
void reserve(size_t n);



//定义
void wxt::string::reserve(size_t n)
{
	if (n > _capacity)//判断,如果预扩容大小大于原容量才扩容
	{
		char* tmp = new char[n + 1];//开辟所需空间
		strcpy(tmp, _str);//拷贝原空间数据到开辟好的空间
		delete[] _str;//释放原空间
		_str = tmp;//原空间指针指向新空间
		_capacity = n;//更新对象容量
	}
}

2.9尾插:

  • 首先判断是否需要扩容,需要则先扩容再处理数据的插入
//声明
void push_back(char ch);



//定义
void wxt::string::push_back(char ch)
{
	if (_size == _capacity)//如果大小等于容量就扩容
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);//初始开4字节空间,之后双倍扩容,可以自己定义
	}
	_str[_size] = ch;//字符插入到对象末尾
	_size++;//对象大小加1
	_str[_size] = '\0';//对象末尾空间设置为'\0'
}

2.10尾插字符串

  • 思路:先判断是否需要扩容,处理好容量问题就可以将字符串拷贝到对象末尾。最后更新对象的大小即可
//声明
void append(const char* str);



//定义
void wxt::string::append(const char* str)
{
	size_t len = strlen(str);//求字符串长度
	if (len + _size > _capacity)//判断是否需要扩容
	{
		reserve(len + _size);
	}
	strcpy(_str + _size, str);//拷贝字符串到末尾
	_size = _size + len;//更新对象大小
}

2.11对+=的运算符重载:

  • 相当于尾插的符号版,直接复用尾插函数即可。
//声明
string& operator+=(char ch);
string& operator+=(const char* str);


//定义
wxt::string& wxt::string::operator+=(char ch)
{
	push_back(ch);
	return *this;
}

//引用返回,复用尾插
wxt::string& wxt::string::operator+=(const char* str)
{
	assert(str);
	append(str);
	return *this;
}

2.12插入单个字符:

  • 在指定位置插入单个字符。
  • 首先判断要插入位置是否合法。然后判断是否需要扩容。在指定位置开始向后挪动数据。将指定字符插入指定位置。更新对象大小。
//声明
void insert(size_t pos, char ch);



//定义
void wxt::string::insert(size_t pos, char ch)
{
	assert(pos <= _size);//判断插入位置是否合法
	if (_capacity == _size)//判断是否需要扩容
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	size_t end = _size + 1;
	while (end > pos)//挪动数据,从pos位置向后挪动一位
								//同时会挪动'\0'!
	{
		_str[end] = _str[end - 1];
		end--;
	}
	_str[pos] = ch;//插入数据
	_size++;//更新对象大小
}

2.13插入字符串

  • 思路同插入单个字符。
//声明
void insert(size_t pos, const char* str);



//定义
void wxt::string::insert(size_t pos, const char* str)
{
	assert(pos <= _size);//断言下标是否合法
	size_t len = strlen(str);//求要插入的字符串的长度
	if (_size + len > _capacity)//超过空间则扩容
	{
		reserve(_size + len);
	}
	size_t end = _size + 1;//从后向前挪动,标记最后要操作的位置
	while (end > pos)//挪动数据
	{
		_str[end + len - 1] = _str[end - 1];
		--end;
	}
	_size += len;//更新对象大小
	strncpy(_str + pos, str, len);//使用strncpy不使用strcpy,防止将字符串的'\0'也拷贝过来
}

2.14运算符的重载:

  • 实现>和=,就可以复用实现其他符号了。
//声明:
bool operator<(const string& s) const;
bool operator==(const string& s) const;
bool operator<=(const string& s) const;
bool operator>(const string& s) const;
bool operator>=(const string& s) const;
bool operator!=(const string& s) const;


//定义
bool wxt::string::operator<(const wxt::string& s) const
{
	return strcmp(_str, s._str) < 0;
}

bool wxt::string::operator==(const string& s) const
{
	return strcmp(_str, s._str) == 0;
}

bool wxt::string::operator<=(const string& s) const
{
	return (*this < s) || (*this == s);
}

bool wxt::string::operator>(const string& s) const
{
	return !(*this < s);
}

bool wxt::string::operator>=(const string& s) const
{
	return (*this > s) || (*this == s);
}

bool wxt::string::operator!=(const string& s) const
{
	return !(*this == s);
}

2.15数据清理函数:

  • 完成空间数据的清理,只是清理数据,所以只会影响对象的大小,不会影响对象的容量。
//声明
void clear();



//定义
void wxt::string::clear()
{
	_size = 0;
	_str[0] = '\0';
}

2.16删除指定字符或字符串

//声明
void erase(size_t pos, size_t len = std::string::npos);



//定义
void wxt::string::erase(size_t pos, size_t len)
{
	assert(pos < _size);//判断下标是否合法,由于删除的是数据,所以合法的下标不能是_size
	if (len == std::string::npos || len + pos >= _capacity)
	{//处理全删和默认不传值的情况,不传值默认全删
		_str[pos] = '\0';//直接将指定位置设置为'\0'
		_size = pos;//更新对象大小,pos处为'\0',也就是_size的值
	}
	else
	{//处理删除部分数据的情况
		size_t begin = pos + len;//设置要挪动数据的起始位置
		while (begin <= _size)//循环挪动数据,结束条件为起始位置和末尾数据所在位置重叠
		{
			_str[begin - len] = _str[begin];
			++begin;
		}
		_size -= len;//更新对象大小
	}
}

2.17更改对象大小

  • 指定大小小于或等于对象大小,说明要截断数据
  • 大于,则说明要扩充,使用指定字符扩充即可
//声明
void rsize(size_t n, char ch = '\0');


//定义
void wxt::string::rsize(size_t n, char ch)
{
	if (n <= _size)//如果指定大小小于对象大小,说明需要删除超过指定大小意外的数据
	{
		_str[n] = '\0';
		_size = n;
	}
	else
	{
		reserve(n);//扩容,如果n小于_capacity,不扩容。
		while (_size < n)//如果n大于_size,说明需要插入指定字符
		{//循环,知道n等于_size再终止
			_str[_size] = ch;
			++_size;
		}
		_str[_size] = '\0';//将_size设置为\0
	}
}

2.18find系列

  • 查找字符,暴力匹配即可
  • 查找子串,使用strstr函数即可
//声明
size_t find(char ch, size_t pos = 0) const;
size_t find(const char* str, size_t pos = 0) const;


//定义
size_t wxt::string::find(char ch, size_t pos) const
{
	//不需要使用assert断言下标是否合法
	//因为,下标不合法和没有查到,都可以返回npos
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
	return std::string::npos;
}

//find查找字符串
size_t wxt::string::find(const char* str, size_t pos) const
{
	//无需assert断言,下标不合法返回npos即可
	const char* p = strstr(_str + pos, str);
	if (p)
	{
		return p - _str;//指针减去指针,得到相差的元素个数
	}
	return std::string::npos;
}

2.19截取对象的一部分作为新的对象

//声明
string substr(size_t pos = 0, size_t len = std::string::npos) const;


//定义
wxt::string wxt::string::substr(size_t pos, size_t len) const
{
	wxt::string s;//创建一个用于返回的对象
	size_t end = pos + len;//设置结束位置
	if (len == std::string::npos || len + pos >= _size)
	{
		len = _size - pos;//求出要截取的长度,用于之后开辟空间
		end = _size;//如果是全取,更新结束位置为最后一个元素的后一个位置,也就是'\0'处
	}
	s.reserve(len);//一次开辟好空间
	for (size_t i = pos; i < end; i++)
	{
		s += _str[i];//将截取的子串,一个字符一个字符的添加到要返回的对象中
	}
	return s;//返回子串对象
}

2.20流提取的重载:

  • in.get可以获取任意字符,包括空格和换行符
//声明
istream& operator>>(istream& in, string& s);


//定义
istream& wxt::operator>>(istream& in, string& s)
{
	s.clear();//清空对象
	char ch;
	ch = in.get();//使用get方法获取一个流字符
	while (ch != ' ' && ch != '\n')//当获取的字符不为空格和换行时
	{
		s += ch;//字符尾插到对象
		ch = in.get();//获取下一个字符
	}
	return in;//返回istream指针
}

2.21流输出的重载:

//声明
ostream& operator<<(ostream& out, const string& s);


//定义
ostream& wxt::operator<<(ostream& out, const wxt::string& s)
{
	for (size_t i = 0; i < s.size(); i++)//循环打印
	{
		out << s[i];
	}
	return out;//返回istream指针
}

三、函数文件

#pragma once
#include "string.h"

//无参构造,可以被下面的带参构造+缺省的方式替代
//wxt::string::string()
//	:_str(new char[1]{'\0'})//空string对象也要包含一个字符\0占位
//	,_size(0)//大小和容量都初始化为0,容量大小不包含\0在内
//	,_capacity(0)
//{}

//带参构造
wxt::string::string(const char* str)//给一个缺省值,处理传空字符串的情况
	:_size(strlen(str))//大小开辟为传来的字符串的长度
	, _capacity(_size)//容量设置为字符串的大小
{
	_str = new char[_capacity + 1];//多开辟一个字节空间,存放\0
	strcpy(_str, str);//将空字符串拷贝过去
}

//析构
wxt::string::~string()
{
	delete[] _str;//释放对象在堆上动态开辟的空间
	_str = nullptr;//将指向堆上空间的指针置空
	_size = _capacity = 0;//将大小和容量置空
}

size_t wxt::string::size() const//返回对象大小,加const修饰*this不被修改
{
	return _size;
}

size_t wxt::string::capacity() const//返回容量大小,加const成员修饰this指针
{
	return _capacity;
}

char& wxt::string::operator[](size_t pos)//传引用返回,可以修改指定下标处字符
{
	assert(pos < _size);//断言下标为合法位置
	return _str[pos];//返回对象下标
}

const char wxt::string::operator[](size_t pos) const
//前面的const修饰函数,表示该函数为const修饰的string对象使用
//后面的const修饰this指针,与上一个[]的运算符重载构成函数重载
{
	assert(pos < _size);
	return _str[pos];
}

wxt::string::iterator wxt::string::begin()
{
	return _str;//返回字符串名字,相当于首元素地址
}

wxt::string::iterator wxt::string::end()
{
	return _str + _size;//返回最后一个字符后一个的地址
								//相当于返回\0的地址,原string对象end-1才是最后一个字符地址
}


//扩容函数,思路是先判断是否需要扩容,
//需要扩容,先开辟空间,然后将原空间内容拷贝过去
//释放原空间,更新指针指向新空间,更新对象容量大小
void wxt::string::reserve(size_t n)
{
	if (n > _capacity)//判断,如果预扩容大小大于原容量才扩容
	{
		char* tmp = new char[n + 1];//开辟所需空间
		strcpy(tmp, _str);//拷贝原空间数据到开辟好的空间
		delete[] _str;//释放原空间
		_str = tmp;//原空间指针指向新空间
		_capacity = n;//更新对象容量
	}
}

//尾插,思路:
//首先判断空间是否足够,不足扩容
//足够,将字符放在末尾,将最后一位的后一位设为\0
void wxt::string::push_back(char ch)
{
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	_str[_size] = ch;
	_size++;
	_str[_size] = '\0';
}

//尾插字符串,思路:
//判断是否需要扩容,将字符串拷贝到对象末尾,更新对象大小
void wxt::string::append(const char* str)
{
	size_t len = strlen(str);//求字符串长度
	if (len + _size > _capacity)//判断是否需要扩容
	{
		reserve(len + _size);
	}
	strcpy(_str + _size, str);//拷贝字符串到末尾
	_size = _size + len;//更新对象大小
}

//运算符重载+=,引用返回,复用尾插,返回对象this指针
wxt::string& wxt::string::operator+=(char ch)
{
	push_back(ch);
	return *this;
}

//引用返回,复用尾插
wxt::string& wxt::string::operator+=(const char* str)
{
	assert(str);
	append(str);
	return *this;
}

//插入,思路:
//先判断是否扩容,挪动数据,插入数据
void wxt::string::insert(size_t pos, char ch)
{
	assert(pos <= _size);//判断插入位置是否合法
	if (_capacity == _size)//判断是否需要扩容
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	size_t end = _size + 1;
	while (end > pos)//挪动数据,从pos位置向后挪动一位
								//同时会挪动'\0'!
	{
		_str[end] = _str[end - 1];
		end--;
	}
	_str[pos] = ch;//插入数据
	_size++;//更新对象大小
}

//void wxt::string::insert(size_t pos, const char* str)
//{
//	assert(pos <= _size);
//	assert(str);
//	size_t len = strlen(str);
//	if (_size + len > _capacity)
//	{
//		reserve(_size + len);
//	}
//	_size += len;
//	size_t end = _capacity;
//	while (end > pos + len)
//	{
//		_str[end - 1] = _str[end - len - 1];
//		--end;
//	}
//	strcpy(_str + pos, str);
//}


//void wxt::string::erase(size_t pos, size_t len = npos)
//{
//	assert(pos <= _size);
//	if (len < _capacity - _size)
//	{
//		while (len > 0)
//		{
//
//		}
//	}
//}

//直接比较对象中的字符串,根据返回值判断比较结果
bool wxt::string::operator<(const wxt::string& s) const
{
	return strcmp(_str, s._str) < 0;
}

bool wxt::string::operator==(const string& s) const
{
	return strcmp(_str, s._str) == 0;
}

bool wxt::string::operator<=(const string& s) const
{
	return (*this < s) || (*this == s);
}

bool wxt::string::operator>(const string& s) const
{
	return !(*this < s);
}

bool wxt::string::operator>=(const string& s) const
{
	return (*this > s) || (*this == s);
}

bool wxt::string::operator!=(const string& s) const
{
	return !(*this == s);
}

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

const char* wxt::string::str() const
{
	return _str;
}

ostream& wxt::operator<<(ostream& out, const wxt::string& s)
{
	for (size_t i = 0; i < s.size(); i++)//循环打印
	{
		out << s[i];
	}
	return out;//返回istream指针
}

istream& wxt::operator>>(istream& in, string& s)
{
	s.clear();//清空对象
	char ch;
	ch = in.get();//使用get方法获取一个流字符
	while (ch != ' ' && ch != '\n')//当获取的字符不为空格和换行时
	{
		s += ch;//字符尾插到对象
		ch = in.get();//获取下一个字符
	}
	return in;//返回istream指针
}

//针对const对象的迭代器。
wxt::string::iterator wxt::string::begin() const
{
	return _str;
}
wxt::string::iterator wxt::string::end() const
{
	return _str + _size;
}

//————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————

//插入字符串
void wxt::string::insert(size_t pos, const char* str)
{
	assert(pos <= _size);//断言下标是否合法
	size_t len = strlen(str);//求要插入的字符串的长度
	if (_size + len > _capacity)//超过空间则扩容
	{
		reserve(_size + len);
	}
	size_t end = _size + 1;//从后向前挪动,标记最后要操作的位置
	while (end > pos)//挪动数据
	{
		_str[end + len - 1] = _str[end - 1];
		--end;
	}
	_size += len;//更新对象大小
	strncpy(_str + pos, str, len);//使用strncpy不使用strcpy,防止将字符串的'\0'也拷贝过来
}

//void wxt::string::insert(size_t pos, const char* str)
//{
//	assert(pos <= _size);//断言下标是否合法
//	size_t len = strlen(str);//求要插入的字符串的长度
//	if (_size + len > _capacity)//超过空间则扩容
//	{
//		reserve(_size + len);
//	}
//	size_t end = _size + len + 1;//从后向前挪动,标记最后要操作的位置
//	while (end > _size)//挪动数据
//	{
//		_str[end - 1] = _str[end - 1 - len];
//		--end;
//	}
//	_size += len;//更新对象大小
//	strncpy(_str + pos, str, len);//使用strncpy不使用strcpy,防止将字符串的'\0'也拷贝过来
//}


//删除字符或字符串
void wxt::string::erase(size_t pos, size_t len)
{
	assert(pos < _size);//判断下标是否合法,由于删除的是数据,所以合法的下标不能是_size
	if (len == std::string::npos || len + pos >= _capacity)
	{//处理全删和默认不传值的情况,不传值默认全删
		_str[pos] = '\0';//直接将指定位置设置为'\0'
		_size = pos;//更新对象大小,pos处为'\0',也就是_size的值
	}
	else
	{//处理删除部分数据的情况
		size_t begin = pos + len;//设置要挪动数据的起始位置
		while (begin <= _size)//循环挪动数据,结束条件为起始位置和末尾数据所在位置重叠
		{
			_str[begin - len] = _str[begin];
			++begin;
		}
		_size -= len;//更新对象大小
	}
}

//分两种情况,n小于_size和n大于_size
//小于则直接设置‘\0’,大于先扩容,然后填充字符
void wxt::string::rsize(size_t n, char ch)
{
	if (n <= _size)
	{
		_str[n] = '\0';
		_size = n;
	}
	else
	{
		reserve(n);//扩容,如果n小于_capacity,不扩容。
		while (_size < n)//如果n大于_size,说明需要插入指定字符
		{//循环,知道n等于_size再终止
			_str[_size] = ch;
			++_size;
		}
		_str[_size] = '\0';//将_size设置为\0
	}
}

//find查找字符
//size_t wxt::string::find(char ch, size_t pos) const
//{
//	//不需要使用assert断言下标是否合法
//	//因为,下标不合法和没有查到,都可以返回npos
//	while (pos < _size)
//	{
//		if (_str[pos] == ch)
//		{
//			return pos;
//		}
//		++pos;
//	}
//	return std::string::npos;
//}

size_t wxt::string::find(char ch, size_t pos) const
{
	//不需要使用assert断言下标是否合法
	//因为,下标不合法和没有查到,都可以返回npos
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
	return std::string::npos;
}

//find查找字符串
size_t wxt::string::find(const char* str, size_t pos) const
{
	//无需assert断言,下标不合法返回npos即可
	const char* p = strstr(_str + pos, str);
	if (p)
	{
		return p - _str;//指针减去指针,得到相差的元素个数
	}
	return std::string::npos;
}

wxt::string wxt::string::substr(size_t pos, size_t len) const
{
	wxt::string s;//创建一个用于返回的对象
	size_t end = pos + len;//设置结束位置
	if (len == std::string::npos || len + pos >= _size)
	{
		len = _size - pos;//求出要截取的长度,用于之后开辟空间
		end = _size;//如果是全取,更新结束位置为最后一个元素的后一个位置,也就是'\0'处
	}
	s.reserve(len);//一次开辟好空间
	for (size_t i = pos; i < end; i++)
	{
		s += _str[i];//将截取的子串,一个字符一个字符的添加到要返回的对象中
	}
	return s;//返回子串对象
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/576947.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uni-app中配置自定义条件编译

前提&#xff1a;官网提供的自定义编译不满足条件 package.json | uni-app官网 下文&#xff1a;不详细写&#xff0c;主要写关键思路 package.json文件 主要看scripts的执行命令&#xff0c;其他依赖就是用vue-cli方式创建uni-app项目生成的 {"name": "un…

命令行启动pytest自动化程序时,程序卡住不动了,不继续往下执行了

一、问题描述 在执行pytestallure自动化测试工具的时候&#xff0c;命令行启动程序时&#xff0c;程序卡住不继续往下执行&#xff0c;如下图所示。 代码主函数如下&#xff1a; 二、解决方法 测试客户项目时遇到2次此类问题&#xff0c;2次问题原因不一样。 原因一&#xf…

3d合并的模型为什么没有模型---模大狮模型网

在3D建模中&#xff0c;合并模型是常见的操作&#xff0c;它可以将多个模型合并成一个整体。然而&#xff0c;有时候在合并后却发现部分模型消失了&#xff0c;这可能会让人感到困惑和失望。本文将探讨为什么合并的3D模型中会出现没有模型的情况&#xff0c;并提供一些解决方法…

API和微服务设计的优化方式有哪些?

在构建响应迅速、用户体验良好的应用程序中&#xff0c;API性能的优化至关重要。在构建高性能的API时&#xff0c;采取综合策略是至关重要的。通过采用一系列策略&#xff0c;我们可以确保API在处理请求时高效运行&#xff0c;提供流畅的服务。 一、API和微服务设计的优化可以…

Edge下载文件提示无法安全下载的解决方法

问题描述&#xff1a;最近Edge在下载文件时总是提示&#xff1a;无法安全下载&#xff0c;本文记录一下解决方法。 提示截图&#xff1a; 解决方式一&#xff1a; 1. 点击下图红框的三个点&#xff0c;选择保留 2. 选择仍然保留 解决方式二&#xff1a; 第一种方式每下载一次…

MySQL双层游标嵌套循环方法

文章目录 1、需求描述2、思路3、创建存储过程 1、需求描述 1、在项目中&#xff0c;需要将A表中主键id&#xff0c;逐个取出&#xff0c;作为条件&#xff0c;在B表中去逐一查询&#xff0c;将B表查询到的结果集&#xff08;A表B表关系&#xff1a;一对多&#xff09;&#xf…

Qt下使用OpenCV截取图像并在QtableWidget表格上显示

文章目录 前言一、在QLabel上显示图片并绘制矩形框二、保存矩形框数据为CSV文件三、保存截取图像四、将截取图像填充到表格五、图形视图框架显示图像六、示例完整代码总结 前言 本文主要讲述了在Qt下使用OpenCV截取绘制的矩形框图像&#xff0c;并将矩形框数据保存为CSV文件&a…

【经验分享】MySQL集群部署一:主从模式

目录 前言一、基本介绍1.1、概念1.2、执行流程 二、部署2.1、通用配置2.2、主节点配置2.3、从节点配置2.4、主从测试2.5、谈一谈主节点历史数据同步问题 前言 MySQL的部署模式常见的包括以下几种&#xff1a; 独立服务器部署主从复制部署高可用性集群&#xff08;HA&#xff…

Angular创建项目

Angular创建项目 文章目录 Angular创建项目1. 创建项目1.1 直接安装1.2 跳过npm i安装 2. 运行程序 1. 创建项目 ng new 项目名称 1.1 直接安装 ng new angulardemo --同时会安装依赖包&#xff0c;执行的命令就是npm i 1.2 跳过npm i安装 ng new angulardemo --skip-inst…

【数据结构7-2】-二叉排序树(建立、遍历、查找、增加、删除)

目录 1 基础说明2 基本思路-二叉树的创建和插入2.1 节点存储结构的建立2.2 二叉树创建函数的设计2.3 二叉树插入函数的设计2.4 简单的进行二叉树的检测看看插入的对不对&#xff1a;2.5 整体代码&#xff1a; 3 二叉树的遍历3.1 中序遍历3.2 程序代码&#xff1a;3.3 程序结果&…

RFID技术引领3C手机镜头模组产线智能化转型

RFID技术引领3C手机镜头模组产线智能化转型 应用背景 随着智能手机市场的快速发展与技术创新&#xff0c;手机镜头模组作为影像功能的核心组件&#xff0c;其生产精度、效率及供应链管理的重要性日益凸显。面对复杂多变的市场需求、严格的品质要求以及激烈的市场竞争&#xf…

MySQL数据库总结

作者&#xff1a;爱塔居-CSDN博客 专栏&#xff1a;数据库 目录 前言 一、数据库操作 1.1 创建数据库 1.2 显示当前数据库 1.3 使用数据库 1.4 删除数据库 二、表的操作 2.1 查看表结构 2.2 创建表 2.3 删除表 三、表的增删改查操作&#xff08;CRUD) 3.1 新增 3.…

改ip地址软件手机怎么弄?分享操作指南与注意事项

随着移动互联网的普及&#xff0c;手机已成为我们日常生活中不可或缺的工具。在某些情况下&#xff0c;我们可能需要更改手机的IP地址&#xff0c;以满足特定的网络需求或实现某些功能。然而&#xff0c;对于许多用户来说&#xff0c;如何在手机上更改IP地址可能是一个相对陌生…

clickhouse与oracle传输数据

参考 https://github.com/ClickHouse/clickhouse-jdbc-bridge https://github.com/ClickHouse/clickhouse-jdbc-bridge/blob/master/docker/README.md clickhouse官方提供了一种方式&#xff0c;可以实现clickhouse与oracle之间传输数据&#xff0c;不仅仅是oracle&#xff0…

ShardingSphere 5.x 系列【25】 数据分片原理之 SQL 解析

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 3.1.0 本系列ShardingSphere 版本 5.4.0 源码地址:https://gitee.com/pearl-organization/study-sharding-sphere-demo 文章目录 1. 分片执行流程1.1 Simple Push Down1.2 SQL Federation2. SQL 解析2.1 解析…

代码随想录算法训练营DAY38|C++动态规划Part.1|动态规划理论基础、509.斐波那契数、70.爬楼梯、746.使用最小花费爬楼梯

文章目录 动态规划理论基础什么是动态规划动态规划的解题步骤DP数组以及下标的含义递推公式DP数组初始化DP数组遍历顺序打印DP数组动态规划五部曲 动态规划应该如何debug 509.斐波那契数什么是斐波那契数列动态规划五部曲确定dp数组下标以及含义确定递推公式dp数组如何初始化确…

场景文本检测识别学习 day07(BERT论文精读)

BERT 在CV领域&#xff0c;可以通过训练一个大的CNN模型作为预训练模型&#xff0c;来帮助其他任务提高各自模型的性能&#xff0c;但是在NLP领域&#xff0c;没有这样的模型&#xff0c;而BERT的提出&#xff0c;解决了这个问题BERT和GPT、ELMO的区别&#xff1a; BERT是用来…

翻译《The Old New Thing》 - Why .shared sections are a security hole

Why .shared sections are a security hole - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20040804-00/?p38253 Raymond Chen 2004年08月04日 许多人会推荐使用共享数据节作为在应用程序的多个实例之间共享数据的一种方式。这听起来是个好…

走向大规模应用之前,DePIN 如何突破技术、数据与市场之网

近期&#xff0c;随着分布式物理基础设施网络&#xff08;DePIN&#xff09;的快速演变&#xff0c;一个旨在利用区块链技术彻底改造传统基础设施模型的新兴生态系统正在逐渐浮现。2024 年 4 月&#xff0c;以 peaq 为代表的 DePIN 项目成功筹集了 1500 万美元用于生态系统的扩…

通过 API从 0 到 1 构建 本地 GPTs——1.构建Builder‘s Prompt

目的&#xff1a;帮助小白用户生成结构化 prompt 功能清单 搭建本地 gpts 能力&#xff0c;构建本地企业知识库助手Builder’s Prompt -对话引导构建 prompt 示例&#xff0c;生成助手信息function_call的用法prompt 示例 GPTs 的 Create 能力 用于引导用户构建结构化的 pr…