C++之String类模拟实现(下)

片头

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

运行一下:

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值