片头
哈喽~小伙伴们,在上一篇中,我们讲解了C++的string类的相关函数,这一章中,我们将继续深入学习string类函数,准备好了吗?咱们开始咯~
五、对内容进行修改
⑤insert函数
在指定位置插入字符或者字符串
函数声明
//insert函数
void insert(size_t pos, char ch);//插入字符
函数定义
//insert函数,插入字符
void string::insert(size_t pos, char ch) {
//严格控制pos的取值范围,避免越界
assert(pos <= _size);
//如果_capacity和_size相等,需要扩容
if (_capacity == _size) {
size_t newCapacity = _capacity == 0 ? 4 : 2 * _capacity;
reserve(newCapacity);
}
//end从'\0'的位置开始
size_t end = _size;
while (end >= pos) {
_str[end + 1] = _str[end];
//最后一次: pos+1 = pos;
--end;
//每执行完一次,end向前挪动一位
}
_str[pos] = ch;//将pos位置放入ch字符
++_size; //_size更新
}
这里的end初始指向末尾_size,也就是'\0'的位置,将数据不断往后挪动,直到腾出pos位置。
测试一下:
但是如果这时,我们往下标为0的位置头插'D',发现编译器崩溃,为啥呢?
因为pos此时为0, 如果pos大于等于0,则进入循环,若pos小于0,就结束。end是size_t类型,就不会小于0。如果end减到-1,再转成无符号类型size_t,无符号整型-1是一个很大很大的数,必然会越界。所以,我们将end的类型修改为int。
int end = _size;
但是此时的pos仍然是size_t类型,在一个操作符两边的操作数,如果它们类型不一样,它们就会发生隐式类型转换,当有符号类型遇到无符号类型,有符号类型就会隐式类型转换成无符号类型。
解决方法1:将pos强转成int类型
int end = _size; //end从'\0'的位置开始
while (end >= (int)pos) //将pos强转成int,避免类型不同
{
_str[end + 1] = _str[end]; //最后一次:_str[pos+1] = _str[pos]
--end;
}
_str[pos] = ch;
++_size;
}
解决方法2:end仍然是size_t类型,但是起始位置是_size+1
size_t end = _size + 1; //end的起始位置是_size的下一个位置
while (end > pos) //end>pos位置继续,end==pos就结束
{
_str[end] = _str[end - 1]; //最后一次: 下标为0的元素挪到下标为1的位置
end--;
}
_str[pos] = ch;
++_size;
我们 初始指向'\0'的下一个位置,最终当end和pos值相等时跳出循环,完成插入与对成员变量的修改。
接下来,我们用insert函数实现插入字符串
函数声明
void insert(size_t pos, const char* str);//插入字符串
函数定义
//方法1:
void string::insert(size_t pos, const char* str) {
//严格控制pos的取值范围,避免越界
assert(pos <= _size);
int len = strlen(str); //新字符串的长度(不包括'\0')
if (_size + len > _capacity) //检查是否需要扩容
{
reserve(_size + len);
}
int end = _size; //end的起始位置_size
while (end >= (int)pos) //判断end是否满足循环条件
{
_str[end + len] = _str[end];
end--;
}
memcpy(_str + pos, str, len); //使用memcpy函数,拷贝len个字符,不包括'\0'
_size += len; //_size更新
}
诶,为啥这里不能使用strcpy函数了呢?
因为strcpy函数会把插入字符串的**'\0'**一起拷贝过去,改变了原字符串的内容,这样就不行的。
中间的逻辑也可以这样进行修改,保证结果正确:
int end = _size + len; //将end设定为_size+len
while (end > pos + len - 1) //判断end是否满足循环条件
{
_str[end] = _str[end - len];
end--;
}
memcpy(_str + pos, str, len); //使用memcpy函数,拷贝len个字符,不包括'\0'
_size += len; //_size更新
测试一下:
实现了insert函数后,我们就可以在push_back函数和append函数复用它们
//尾插一个字符
void string::push_back(char ch) {
insert(_size, ch); //在_size位置,插入一个字符ch
}
//尾插一个字符串
void string::append(const char* str) {
insert(_size, str);//在_size位置,插入一个字符串
}
⑥erase函数
函数声明
//erase函数
void erase(size_t pos = 0 , size_t len = npos);
从pos位置开始,删除len个字符(pos默认为0,len默认为npos)
这里的pos不能等于_size,因为我们不能删去'\0',npos为整数-1,这里我们需要自己在类中定义。
public:
static const int npos;
在外部进行初始化
const int string::npos = -1;
函数定义
//erase函数
void string::erase(size_t pos, size_t len = npos) {
assert(pos < _size); //严格控制pos的有效区间
//len大于后面字符个数时,有多少删多少
if (len == npos || len >= _size - pos)
{
_str[pos] = '\0'; //将pos的位置改为'\0'
_size = pos; //有效元素的个数为pos个
}
else
{
//len小于后面的字符个数
strcpy(_str + pos, _str + pos + len);//将后面的字符拷贝到pos位置
_size -= len; //有效字符个数更新
}
}
运行一下:
⑦find查找字符或字符串
函数声明
//find函数
size_t find(char ch, size_t pos = 0); //查找字符
size_t find(const char* sub, size_t pos = 0); //查找字符串
函数定义
//从pos位置开始,查找字符
size_t string::find(char ch, size_t pos) {
for (int i = pos; i < _size; i++) {
if (_str[i] == ch) {
return i;
}
}
return npos;
}
//从pos位置开始,查找字符串
size_t string::find(const char* sub, size_t pos) {
char* p = strstr(_str + pos, sub);
return p - _str;//返回的就是下标
}
**(1)查找字符:**我们给了缺省值npos,如果不赋值,默认从起始位置开始找,遍历数组;若找到目标字符,则返回下标;若找不到,则返回npos
**(2)查找目标字符串:**我们调用字符串函数strstr,如果找到返回目标字符串起始位置的指针;若没找到,则返回空指针。我们再加一个判断条件,如果不为空,指针相减即为目标字符串起始位置的下标(第一个元素的下标);若为空,则找不到,返回npos。
因此,我们还可以把查找字符串函数再改进一下:
//从pos位置开始,查找字符串
size_t string::find(const char* sub, size_t pos) {
//p指针表示找到目标字符串的地址
char* p = strstr(_str + pos, sub);
//如果p指针存在,直接返回目标字符串的下标
if (p) {
return p - _str;
}
else {
//如果p指针不存在,返回npos
return npos;
}
}
运行一下
⑧substr函数
substr函数用于从字符串中提取一个子字符串
函数声明
//substr函数
string substr(size_t pos = 0, size_t len = npos);
函数定义
//substr函数
//定义方法1:
string string::substr(size_t pos, size_t len) {
string sub;
if (len == npos || len >= _size - pos) {
for (size_t i = pos; i < _size; i++) {
sub += _str[i];
}
}
else {
for (size_t i = pos; i < pos + len; i++) {
sub += _str[i];
}
}
return sub;
}
我们直接在新的sub字符串尾插字符
还有另外一种定义方法:
//substr函数
//定义方法2:
string string::substr(size_t pos, size_t len) {
if (len == npos || len >= _size - pos) {
string sub(_str + pos);
return sub;
}
else {
string sub;
sub.reserve(len);//至少开len个字符的空间
for (int i = 0; i < len; i++) //执行拷贝的次数
{
sub += _str[pos + i];//从pos位置开始,拷贝len个字符
}
return sub;
}
}
⑨swap函数
函数声明
//swap函数
void swap(string& s);
函数定义
//swap函数
void string::swap(string& s) {
//使用std库里面的swap函数
std::swap(_str, s._str);
std::swap(_capacity, s._capacity);
std::swap(_size, s._size);
}
运行一下
六、重载函数
①operator=赋值函数
函数声明
//operator=赋值函数
string& operator=(const string& s);
函数定义
//operator=赋值函数
string& string::operator=(const string& s) {
char* temp = new char[s._capacity + 1]; //多开一个空间,存放'\0'
strcpy(temp, s._str); //数据拷贝
delete[] _str; //释放原来的空间
_str = temp; //将指针指向新地址
_size = s._size; //_size更新
_capacity = s._capacity; //_capacity更新
return *this; //返回*this
}
运行一下
但是如果是自己赋值给自己,那么就不需要释放原来的旧空间。因此,我们需要将代码改进
//operator=赋值函数
string& string::operator=(const string& s) {
if (this != &s) //如果不是自身赋值
{
char* temp = new char[s._capacity + 1]; //多开一个空间,存放'\0'
strcpy(temp, s._str); //数据拷贝
delete[] _str; //释放原来的空间
_str = temp; //将指针指向新地址
_size = s._size; //_size更新
_capacity = s._capacity; //_capacity更新
}
return *this; //返回*this
}
}
开辟一块新空间,将原内容拷贝到新空间中并释放原来的空间,然后更改指针指针指向和成员变量,最后返回*this
当然了,我们还可以将代码改进一下:
// s1 = s3
string& string::operator=(const string& s) {
if (this != &s) //如果不是自身赋值
{
string temp(s._str);//调用构造函数,用s字符串去构造temp字符串
swap(temp); //将temp字符串和this进行交换
}
return *this; //返回*this
}
这个写法简单来说, 本来是s3赋值给s1,赋值完毕后,应该释放原来的s1空间。
这里,我们先调用构造函数,实参s3传递给形参s,用s._str构造string类型的temp对象,temp和s开了一样大的空间,s里面的值拷贝给temp,最后是temp和this进行交换,temp的空间给了s1,s1把自己不用的空间给了temp,最后temp调用析构函数,完成对s1原来空间的释放。
就比如:我点单了,外卖员送餐到我家了,我对他还提了一个要求:帮我把垃圾带走。外卖员说,好吧好吧。
还可以进一步优化
// s1 = s3
string& string::operator=(string temp) {
swap(temp); //将temp字符串和this进行交换
return *this; //返回*this
}
//swap函数
void string::swap(string& s) {
//使用std库里面的swap函数
std::swap(_str, s._str);
std::swap(_capacity, s._capacity);
std::swap(_size, s._size);
}
为什么不能传递引用&,参数为string& temp呢?
因为传递了引用&,temp是s3的别名,看似是temp和this交换,实质上是s3和s1进行交换,s3只是赋值给s1,s3不能被修改。就像a = b,把b的值赋给a,但是b不能改变。
所以,我们需要用s3调用拷贝构造(string temp = s3;),构造出一个临时的string类型的对象temp,方便和s1进行交换。参数是传值,传值传参或者传值返回用的都是拷贝后的临时变量,对于自定义类型需要调用拷贝构造拷贝临时变量。
直接用temp和this进行交换,s1的空间换给temp,temp是个局部对象,出了作用域,调用析构函数。
②operator==等几个比较函数
函数声明
//比较函数
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;
函数定义
//比较2个字符串元素的ASCII码值
bool string::operator<(const string& s)const
{
return strcmp(_str, s._str) < 0;
}
bool string::operator==(const string& s)const
{
return strcmp(_str, s._str) == 0;
}
我们可以先写出2个简单的函数,剩下的情况可以复用上面的函数
其余的函数如下:
//比较函数
bool string::operator<=(const string& s)const
{
return *this < s || *this == s;
}
bool string::operator>(const string& s)const
{
return !(*this <= s);
}
bool string::operator>=(const string& s)const
{
return !(*this < s);
}
bool string::operator!=(const string& s)const
{
return !(*this == s);
}
③流插入和流提取
注意:流插入和流提取不能写在string类里面。比如:cout<<d1,这样只能传递2个参数,如果放到了成员函数,就会多一个this指针,这个参数就不方便传递。因此,我们把它写在类外面。
函数声明
//流插入和流提取
istream& operator>>(istream& is, string& str);
ostream& operator<<(ostream& os, const string& str);
函数定义
//流提取
ostream& operator<<(ostream& os, const string& str) {
for (size_t i = 0; i < str.size(); i++) {
os << str[i];
}
return os;
}
我们不需要将上述函数设置为友元,因为我们没有访问成员变量,是通过迭代器来实现的
那么,怎么实现流插入呢?
首先,我们需要获取一个一个字符
//流输入
//不完整代码(错误代码)
istream& operator>>(istream& is, string& str) {
char ch;
is >> ch;
while (ch != ' ' && ch != '\n')
//默认遇到空格或者换行,读取结束
{
str += ch;//将读取到的字符尾插到str字符串后面
}
return is;
}
但是这样就会出现一个问题:每次都是读取的是同一个字符,不会读取到下一个字符。
比如:我键盘输入字符串"abcdefg",结果is只读取到第一个字母'a'。因此,我们必须在while循环里面再进行读取。
//流输入
//改进版本(仍然不完善)
istream& operator>>(istream& is, string& str) {
char ch;
is >> ch;
while (ch != ' ' && ch != '\n')
//默认遇到空格或者换行,读取结束
{
str += ch;
cin >> ch;//继续读取下一个字符
}
return is;
}
但是又有新的问题出现了:此时的C++中的is和cin一样,把空格或者换行当做分割符,会自动过滤掉换行或者空格,ch永远都不可能是空格或换行,就会陷入死循环。
怎么办呢?因为cin是istream类型的对象,我们可以用C++库里面提供的get()函数
//流输入
//仍然不完善
istream& operator>>(istream& is, string& str) {
char ch = is.get();
while (ch != ' ' && ch != '\n')
//默认遇到空格或者换行,读取结束
{
str += ch;
ch = is.get();//继续读取下一个字符
}
return is;
}
get()函数可以读取到空格或者换行
现在我们可以运行一下:
清空原字符串的内容,我们需要定义clear成员函数
//字符串清理
//函数声明
void clear();
//函数定义
void string::clear() {
_str[0] = '\0'; //将原字符串的下标为0的位置为'\0'
_size = 0; //有效元素个数为0个
}
//流输入
istream& operator>>(istream& is, string& str) {
str.clear(); //调用clear函数,清空原来的字符串的内容
char ch = is.get();
while (ch != ' ' && ch != '\n')
//默认遇到空格或者换行,读取结束
{
str += ch;
ch = is.get();//继续读取下一个字符
}
return is;
}
当前流输入函数是可以的,但是当我们输入大量的字符串,就要频繁调用operator+=函数,不断扩容,效率低下,有什么更好的方法呢?
有的小伙伴可能会说:我可以写一个reserve函数,预留空间。但是,reserve函数里面应该开多大的空间呢?
比如:reserve(100),开了可以存放100个元素的空间。但是我如果只存放2、3个字符呢?会造成空间大量的浪费。有没有什么办法能解决这个问题?
有!我们可以定义一个buff数组,元素类型为char,能存放128个元素
char buff[128]; //定义一个buff数组,元素类型为char
int i = 0; //元素的起始下标为0
每次获取到的字符不会直接调用operator+=函数,而是把它存放到buff数组中
while (ch != ' ' && ch != '\n')
//默认遇到空格或者换行,读取结束
{
//str += ch;
buff[i++] = ch;//把读取到的字符存放到buff数组里面
ch = is.get(); //继续读取下一个字符
}
每读取完一个字符,下标i更新,因为buff数组只能存放128个元素,下标范围在0~127,有效元素的下标范围是0~126,最后一个下标127是留给'\0'的
while (ch != ' ' && ch != '\n')
//默认遇到空格或者换行,读取结束
{
buff[i++] = ch;//把读取到的字符存放到buff数组里面
//如果此时i为127,说明下标为0~126的位置已经有元素了
if (i == 127)
{
buff[i] = '\0'; //最后一个位置存放'\0'
str += buff; //此时的str就是一次性加上127个字符
//一次性把空间开好,避免频繁扩容
i = 0; //再次把i置为0
}
ch = is.get(); //继续读取下一个字符
}
此时,如果遇到空格或者换行,不会进入循环,我们需要单独判断。
//此时,遇到空格或者换行,跳出循环
if (i != 0)
{
buff[i] = '\0';//将当前i的位置置为'\0'
str += buff; //将str更新
}
完整代码如下:
//流插入
//字符串清理
void string::clear() {
_str[0] = '\0';
_size = 0;
}
istream& operator>>(istream& is, string& str) {
str.clear();
char ch;
ch = is.get();
char buff[128]; //缓冲数组
size_t i = 0;
while (ch != ' ' && ch != '\n') //读字符读不到空格
{
buff[i++] = ch;
if (i == 127) {
buff[127] = '\0';
str += buff;
i = 0;
}
//s += ch;
ch = is.get();
}
if (i > 0) {
buff[i] = '\0';
str += buff;
}
return is;
}
输入是对内容的覆盖,所以我们首先实现一个clear函数来清空字符串,原理很简单:
//字符串清理
void string::clear() {
_str[0] = '\0';
_size = 0;
}
只需要修改_size并设置'\0'即可
注意,我们cin输入是读不到空格和换行符的。
其中,ch = is.get();从输入流中读取单个字符
我们这里创造了一个缓冲数组char buff[128];,来简单进行讲解一下:
这个数组功能就是存储字符的内容,如果数组满了,则直接进行+=将整个数组内容尾插到字符串中,并更新索引,其主要特点就是,避免了我们自己为字符串s不断扩容降低了效率,并且可能会造成空间的浪费
与cin不同的是,getline可以读取空格,所以我们只需要修改循环条件即可
istream& getline(istream& in, string& s) {
s.clear();
char ch;
ch = in.get();
char buff[128];
size_t i = 0;
while (ch != '\0') {
buff[i++] = ch;
if (i == 127) {
buff[127] = '\0';
s += buff;
i = 0;
}
//s += ch;
ch = in.get();
}
if (i > 0) {
buff[i] = '\0';
s += buff;
}
return in;
}
七、完整代码
string.h文件
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<string.h>
#include<assert.h>
namespace bit
{
class string
{
public:
typedef char* iterator;//将char*重命名为iterator
typedef const char* const_iterator;
//非const版本的iterator
iterator begin(); //提供iterator begin()函数
iterator end(); //提供iterator end()函数
//const版本的iterator
const_iterator begin() const;
const_iterator end() const;
//string();//无参构造
string(const char* str = ""); //全缺省的构造函数
~string(); //析构函数
string(const string& s); //拷贝构造函数
const char* c_str() const; //c_str函数
size_t size() const; //size()函数
size_t capacity() const; //capacity()函数
//非const版本
char& operator[](size_t pos); //operator[]函数
//const版本
const char& operator[](size_t pos)const;
//预留空间
void reserve(size_t n);
//尾插一个字符
void push_back(char ch);
//尾插一个字符串
void append(const char* str);
//operator+=函数可以构成重载,函数名相同,参数不同
string& operator+=(char ch);
string& operator +=(const char* str);
//insert函数
void insert(size_t pos, char ch);//插入字符
void insert(size_t pos, const char* str);//插入字符串
//erase函数
void erase(size_t pos = 0, size_t len = npos);
//find函数
size_t find(char ch, size_t pos = 0); //查找字符
size_t find(const char* sub, size_t pos = 0); //查找字符串
//substr函数
string substr(size_t pos = 0, size_t len = npos);
//operator=赋值函数
string& operator=(const string& s);
//swap函数
void swap(string& s1);
//比较函数
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();
private:
char* _str;
size_t _size;
size_t _capacity;
//静态成员变量npos在类里面声明
const static size_t npos;
};
//流插入和流提取
istream& operator>>(istream& is, string& str);
ostream& operator<<(ostream& os, const string& str);
//getline函数模拟流输入
istream& getline(istream& in, string& s);
}
string.cpp文件
#include"string.h"
namespace bit {
//静态成员变量npos在类外定义
const size_t string::npos = -1;
//指定类域,在类里面声明,在类外面定义
//begin()函数返回的是第一个元素
string::iterator string::begin() {
return _str;
}
//end()函数返回的是最后一个元素的下一个位置
string::iterator string::end() {
return _str + _size;
}
string::const_iterator string::begin() const {
return _str;
}
string::const_iterator string::end() const {
return _str + _size;
}
//无参构造
//string::string() {
// //_str = new char('\0');
// _str = new char[1]{'\0'};
// _size = 0;
// _capacity = 0;
//}
//构造函数
string::string(const char* str)
//将_size放在初始化列表里面
:_size(strlen(str))
{
//_str和_capacity放到函数体里面
_str = (new char[_size + 1]);
_capacity = _size;
strcpy(_str, str);
}
//析构函数
string::~string() {
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//拷贝构造函数
string::string(const string& s) {
_str = new char[s._capacity + 1]; //多开1个空间,存放'\0'
strcpy(_str, s._str); //拷贝数据
_capacity = s._capacity; //设置容量
_size = s._size; //设置有效数据的个数
}
//c_str函数
const char* string::c_str() const {
return _str;
}
//size()函数
size_t string::size() const {
return _size;
}
//capacity()函数
size_t string::capacity() const {
return _capacity;
}
//operator[]函数
char& string::operator[](size_t pos) {
assert(pos < _size);//严格控制pos的有效区间
return _str[pos];
}
//const版本
const char& string::operator[](size_t pos)const {
assert(pos < _size);
return _str[pos];
}
void string::reserve(size_t n) {
//如果n大于当前的_capacity,需要扩容
if (n > _capacity) {
//开n+1个空间,多开1个空间预留给'\0'
//'\0'是不包括在_capacity里面的
char* temp = new char[n + 1];
strcpy(temp, _str); //拷贝数据
delete[] _str; //释放旧空间
_str = temp; //将新的地址赋给_str
_capacity = n; //_capacity为n,代表n个有效数据
}
}
//尾插一个字符
void string::push_back(char ch) {
如果_capacity == _size,说明空间为0或者空间满了,需要扩容
//if (_capacity == _size) {
// size_t newCapacity = _capacity == 0 ? 4 : 2 * _capacity;
// reserve(newCapacity);
//}
//_str[_size] = ch; //新的字符ch插入到原来存放'\0'的位置
//_str[_size + 1] = '\0'; //'\0'就存放到下一个位置
//_size++; //_size更新
insert(_size, ch); //在_size位置,插入一个字符ch
}
//尾插一个字符串
void string::append(const char* str) {
获取str新字符串的长度
//size_t len = strlen(str);
如果_size+len大于原有的capacity,扩容
//if (_size + len > _capacity) {
// reserve(_size + len);
//}
strcat(_str, str);//在原来的基础上,尾插str新字符串
//strcpy(_str+_size, str);//自定义起始位置,从'\0'的位置开始
//_size += len; //_size更新
insert(_size, str);//在_size位置,插入一个字符串
}
string& string::operator+=(char ch) {
push_back(ch);//调用push_back函数
return *this;
}
string& string::operator +=(const char* str) {
append(str);//调用append函数
return *this;
}
//insert函数
void string::insert(size_t pos, char ch) {
//严格控制pos的取值范围,避免越界
assert(pos <= _size);
//如果_capacity和_size相等,需要扩容
if (_capacity == _size) {
size_t newCapacity = _capacity == 0 ? 4 : 2 * _capacity;
reserve(newCapacity);
}
end从'\0'的位置开始
//int end = _size;
将pos强转成int,避免类型不同
//while (end >= (int)pos) {
// _str[end + 1] = _str[end];
// //最后一次: pos+1 = pos;
// --end;
// //每执行完一次,end向前挪动一位
//}
size_t end = _size + 1; //end的起始位置是_size的下一个位置
while (end > pos) //end>pos位置继续,end==pos就结束
{
_str[end] = _str[end - 1]; //最后一次: 下标为0的元素挪到下标为1的位置
end--;
}
_str[pos] = ch;//将pos位置放入ch字符
++_size; //_size更新
}
void string::insert(size_t pos, const char* str) {
//严格控制pos的取值范围,避免越界
assert(pos <= _size);
int len = strlen(str); //新字符串的长度(不包括'\0')
if (_size + len > _capacity) //检查是否需要扩容
{
reserve(_size + len);
}
//int end = _size; //end的起始位置_size
//while (end >= (int)pos) //判断end是否满足循环条件
//{
// _str[end + len] = _str[end];
// end--;
//}
int end = _size + len; //将end设定为_size+len
while (end > pos + len - 1) //判断end是否满足循环条件
{
_str[end] = _str[end - len];
end--;
}
memcpy(_str + pos, str, len); //使用memcpy函数,拷贝len个字符,不包括'\0'
_size += len; //_size更新
}
//erase函数
void string::erase(size_t pos, size_t len) {
assert(pos < _size); //严格控制pos的有效区间
//len大于后面字符个数时,有多少删多少
if (len == npos || len >= _size - pos)
{
_str[pos] = '\0'; //将pos的位置改为'\0'
_size = pos; //有效元素的个数为pos个
}
else
{
//len小于后面的字符个数
strcpy(_str + pos, _str + pos + len);//将后面的字符拷贝到pos位置
_size -= len; //有效字符个数更新
}
}
//find函数
//从pos位置开始,查找字符
size_t string::find(char ch, size_t pos) {
for (int i = pos; i < _size; i++) {
if (_str[i] == ch) {
return i;
}
}
return npos;
}
//从pos位置开始,查找字符串
size_t string::find(const char* sub, size_t pos) {
//char* p = strstr(_str + pos, sub);
//return p - _str;//返回的就是下标
//p指针表示找到目标字符串的地址
char* p = strstr(_str + pos, sub);
//如果p指针存在,直接返回目标字符串的下标
if (p) {
return p - _str;
}
else {
//如果p指针不存在,返回npos
return npos;
}
}
//substr函数
string string::substr(size_t pos, size_t len) {
string sub;
if (len == npos || len >= _size - pos) {
for (size_t i = pos; i < _size; i++) {
sub += _str[i];
}
}
else {
for (size_t i = pos; i < pos + len; i++) {
sub += _str[i];
}
}
return sub;
}
//substr函数
//string string::substr(size_t pos, size_t len) {
// if (len == npos || len >= _size - pos) {
// string sub(_str + pos);
// return sub;
// }
// else {
// string sub;
// sub.reserve(len);//至少开len个字符的空间
// for (int i = 0; i < len; i++) //执行拷贝的次数
// {
// sub += _str[pos + i];//从pos位置开始,拷贝len个字符
// }
// return sub;
// }
//}
//operator=赋值函数
string& string::operator=(const string& s) {
if (this != &s) //如果不是自身赋值
{
char* temp = new char[s._capacity + 1]; //多开一个空间,存放'\0'
strcpy(temp, s._str); //数据拷贝
delete[] _str; //释放原来的空间
_str = temp; //将指针指向新地址
_size = s._size; //_size更新
_capacity = s._capacity; //_capacity更新
}
return *this; //返回*this
}
//swap函数
void string::swap(string& s) {
//使用std库里面的swap函数
std::swap(_str, s._str);
std::swap(_capacity, s._capacity);
std::swap(_size, s._size);
}
//比较函数
bool string::operator<(const string& s)const {
return strcmp(_str, s._str) < 0;
}
bool string::operator<=(const string& s)const {
return *this < s || *this == s;
}
bool string::operator>(const string& s)const {
return !(*this <= s);
}
bool string::operator>=(const string& s)const {
return !(*this < s);
}
bool string::operator==(const string& s)const {
return strcmp(_str, s._str) == 0;
}
bool string::operator!=(const string& s)const {
return !(*this == s);
}
//字符串清理
void string::clear() {
_str[0] = '\0';
_size = 0;
}
//流插入
istream& operator>>(istream& is, string& str) {
str.clear();
char ch;
ch = is.get();
char buff[128]; //缓冲数组
size_t i = 0;
while (ch != ' ' && ch != '\n') //读字符读不到空格
{
buff[i++] = ch;
if (i == 127) {
buff[127] = '\0';
str += buff;
i = 0;
}
//s += ch;
ch = is.get();
}
if (i > 0) {
buff[i] = '\0';
str += buff;
}
return is;
}
//流提取
ostream& operator<<(ostream& os, const string& str) {
for (size_t i = 0; i < str.size(); i++) {
os << str[i];
}
return os;
}
istream& getline(istream& in, string& s) {
s.clear();
char ch;
ch = in.get();
char buff[128];
size_t i = 0;
while (ch != '\0') {
buff[i++] = ch;
if (i == 127) {
buff[127] = '\0';
s += buff;
i = 0;
}
//s += ch;
ch = in.get();
}
if (i > 0) {
buff[i] = '\0';
s += buff;
}
return in;
}
}
片尾
今天,在上一篇的基础上,我们深入学习了string类函数的模拟实现,希望看完这篇文章能对友友们有所帮助!!!
求点赞收藏加关注!!!
谢谢大家!!!