片头
哈喽~小伙伴们,在上一篇中,我们讲解了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; //有效字符个数更新
}
}
运行一下: