2024年最全C++_String增删查改模拟实现(2),海尔C C++面试总结

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

{
//扩容
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
//插入数据
_str[_size] = ch;
_size++;
_str[_size] = ‘\0’;

}


### 5.2 append尾部插入字符串



void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)//扩容
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}


### 5.3 operator+=()字符、字符串


operator+=()字符、字符串可以直接复用push\_back和append。



string& operator+=(char ch)
{
push_back(ch);
return *this;
}

string& operator+=(const char* str)
{
append(str);
return *this;
}


### 5.4 insert插入字符、字符串


#### 5.4.1 insert插入字符(在这提醒下,博主是所有的拷贝数据都是从’\0’开始,这样就不需要单独对’\0’做处理)


insert插入字符逻辑上还是很简单的。  
 首先判断插入字符时是否需要扩容。然后从下标为pos开始,所有数据依次往后挪动。最后将待插入字符给到pos处即可。


##### 初学者最容易范的一个错误


但对于初学者来说,貌似也不太轻松。。。。。。  
 下面给出各位初学者容易犯的错误:



void insert(size_t pos, char ch)
{
assert(pos <= _size);
//扩容
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
//挪动数据
size_t end = _size;
while (end >= pos)
{
_str[end+1] = _str[end];
end–;
}
_str[pos] = ch;
_size++
}


这样对吗?答案是错误的。



> 
> 假设是在头插字符,end理论上和pos(即0)比较完后就减到-1,在下一次循环条件比较时失败,退出循环。  
>  遗憾的是end是size\_t类型,始终>=0, 会导致死循环。
> 
> 
> 


博主在这给出两种解决方法:


1. 将pos强转为整型。



void insert(size_t pos, char ch)
{
assert(pos <= _size);
//扩容
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
//挪动数据
int end = _size;
while (end >= (int)pos)
{
_str[end+1] = _str[end];
end–;
}
_str[pos] = ch;
_size++
}


2.从end从最后数据的后一位开始,每次将前一个数据移到当前位置。最后条件判断就转化为end>pos,不会出现死循环这种情况。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/b6aeb1939a7249068f81a936eaaed55d.png)



void insert(size_t pos, char ch)
{
assert(pos <= _size);
//扩容
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
//挪动数据
size_t end = _size+1;
while (end > pos)
{
_str[end] = _str[end-1];
end–;
}
//插入数据,更新_size
_str[pos] = ch;
_size++;
}


#### 5.4.2 insert插入字符串


insert同样存在相同问题,并且思路一样。博主就直接给出代码了。  
 法一:



void insert(size_t pos, const char* str)
{
int len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}

int end = _size;
while (end >= (int)pos)
{
	_str[end + len] = _str[end];
	end--;
}
strncpy(_str + pos, str, len);
_size += len;

}


法二:



void insert(size_t pos, const char* str)
{
int 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--;
}
strncpy(_str + pos, str, len);
_size += len;

}


### 5.5 erase


erase分两种情况:


1. 从pos开始,要删的数据个数超过的字符串,即将pos后序所有数据全部情况。(直接将pos处数据置为’\0’即可)
2. 从pos开始,要删的数据个数没有超出的字符串。所以只需要从pos+len位置后的所有数据向前移动从pos位置覆盖原数据即可。



void erase(size_t pos, size_t len = npos)
{
if (len==npos || pos + len >= _size)
{
//有多少,删多少
_str[pos] = ‘\0’;
_size = pos;
}
else
{
size_t begin = pos + len;
while (begin <= _size)
{
_str[begin - len] = _str[begin];
begin++;
}
_size -= len;
}
}


## 六、 关系操作符重载:< 、 ==、 <=、 >、>=、!=



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

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

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

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

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

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


## 七、find查找字符、字符串、substr


### 7.1 find查找字符



size_t find(char ch, size_t pos = 0)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}

return npos;

}


### 7.2 find查找字符串



size_t find(const char* sub, size_t pos = 0)
{
const char* p = strstr(_str + pos, sub);
if §
{
return p - _str;
}
else
{
return npos;
}
}


### 7.3 strsub( ) 模拟实现


strsub目标长度可能越界string,也可能还有没有。但不管是那种情况,最后都需要拷贝数据。所以这里我们可以先将len真实长度计算出来,在拷贝数据。



string substr(size_t pos, size_t len = npos)const
{
string s;
size_t end = pos + len;
//目标字符越界string,更新len
if (len == npos || end >= _size)
{
len = _size - pos;
end = _size;
}

//拷贝数据
s.reserve(len);
for (size_t i = pos; i < end; i++)
{
	s += _str[i];
}

return s;

}


## 八、流插入和流提取(<<、>>)(实现在string类外)


### 8.1 流插入<<


由于前面我们实现了迭代器,所以最简单的方式就是范围for



ostream& operator<<(ostream& out, const string& s)
{
/*for (size_t i = 0; i < s.size(); i++)
{
out << s[i];
}*/
for (auto ch : s)
out << ch;

return out;

}


### 8.1 流提取>>


流提取比较特殊。在流提取前需要将原有数据全部清空。同时由于>>无法获取空字符和换行符()(都是作为多个值之间的间隔),直接流提取到ostream对象中,没法结束。(类似于C语言中scanf, 换行符和空字符仅仅只是起到判断结束的作用,但scanf无法获取到它们)  
 所以这里博主直接调用istream对象中的get()函数。(类似于C语言中的getchar()函数)  
 [get详细文档](https://bbs.csdn.net/topics/618668825)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2af9ccef4f1e418f9311088576cdb6cb.png)



class string
{
void clear()
{
_str[0] = ‘\0’;
_size = 0;
}
private:
char* _str;
size_t _capacity;
size_t _size;
};

istream& operator>>(istream& in, string& s)
{
s.clear();
char ch;
//in >> ch;
ch = in.get();

	while (ch != ' ' && ch != '\n')
	{
		s += ch;
		//in >> ch;
		ch = in.get();
	}
	return in;
}

上面这种方法虽然可以达到目的。但还有一个问题,每次插入数据都面临可扩容问题。那如何优化呢?


#### 优化


其中一种办法就是调用reserve()提前开好空间,但这样面临这另一个问题:开大了浪费空间;开小了,同样面临这扩容的问题。  
 所以在这博主采用和vs底层实现的思路:首先开好一段数组(包含’\0’,以16为例)。当数据个数小于16时,字符串存在数组中;当数据个数大于等于16时,将数据存在\_str指向的空间。  
 这是一种以空间换时间的思路,同时也能很好的减少内存碎片的问题。



class string
{
void clear()
{
_str[0] = ‘\0’;
_size = 0;
}
private:
char* _str;
size_t _capacity;
size_t _size;
};

istream& operator>>(istream& in, string& s)
{
s.clear();

char buff[16];
size_t i = 0;

char ch;
ch = in.get();
while (ch != ' ' && ch != '\n')
{
	buff[i++] = ch;
	if (i == 16)
	{
		buff[i] = '\0';
		s += buff;
		i = 0;
	}
ch = in.get();
}

if (i != 0)
{
	buff[i] = '\0';
	s += buff;
}
return in;

}


## 九、所有代码



namespace achieveString
{
class string
{
public:
typedef char* iterator;
const typedef char* const_iterator;

iterator begin()
{
	return _str;
}
iterator end()
{
	return _str + _size;
}

const_iterator begin() const
{
	return _str;
}
const_iterator end() const
{
	return _str + _size;
}

	//构造函数
	/\*string()

:_str(new char[1]{‘\0’})
,_capacity(0)
,_size(0)
{ }*/
string(const char* str = “”)
:_capacity(strlen(str))
, _size(_capacity)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}

	const char\* c\_str() const
	{
		return _str;
	}

	//析构函数
	~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = _capacity = 0;
	}

	//拷贝构造
	/\*string(const string& s)

{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}*/

	//交换
	void swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}

	//现代写法
	string(const string& s)
		:\_str(nullptr)
		,\_size(0)
		,\_capacity(0)
	{
		string tmp(s._str);
		swap(tmp);
	}

	// 赋值重载
	/\*string& operator=(const string& s)

{
if (this != &s)
{
char* tmp = new char[s._capacity + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}*/
//现代写法
//法一
/*string& operator=(const string& s)
{
if (this != &s)
{
string tmp(s._str);
swap(tmp);
}
return *this;
}*/
//法二
string& operator=(string tmp)
{
swap(tmp);
return *this;
}

	//可读可写
	char operator[](size_t pos)
	{
		assert(pos < _size);
		return _str[pos];
	}
	//只读
	const char operator[](size_t pos)const
	{
		assert(pos < _size);
		return _str[pos];
	}

	size_t size()const
	{
		return _size;
	}
	size_t capacity()const
	{
		return _capacity;
	}

	bool empty()const
	{
		return _size == 0;
	}

	void reserve(size_t n)
	{
		if (n > _capacity)
		{
			char\* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}

	void resize(size_t n, char ch='\0')
	{
		if (n <= _size)
		{
			_str[n] = '\0';
			_size = n;
		}
		else
		{
			reserve(n);
			while (_size < n)
			{
				_str[_size] = ch;
				_size++;
			}
			_str[_size] = '\0';
		}
	}

	void push\_back(char ch)
	{
		//扩容
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity \* 2);
		}
		//插入数据
		_str[_size] = ch;
		_size++;
		_str[_size] = '\0';
	
	}

	void append(const char\* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}
		strcpy(_str + _size, str);
		_size += len;
	}

	string& operator+=(char ch)
	{
		push\_back(ch);
		return \*this;
	}

	string& operator+=(const char\* str)
	{
		append(str);
		return \*this;
	}
	
	void insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		//扩容
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity \* 2);
		}
		//挪动数据
		size_t end = _size+1;
		while (end > pos)
		{
			_str[end] = _str[end-1];
			end--;
		}
		//插入数据,更新\_size
		_str[pos] = ch;
		_size++;
	}
	void insert(size_t pos, const char\* str)
	{
		int len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}

		//法一
		/\*int end = \_size;

while (end >= (int)pos)
{
_str[end + len] = _str[end];
end–;
}
strncpy(_str + pos, str, len);
_size += len;*/

		//法二
		size_t end = _size+1;
		while (end > pos)
		{
			_str[end + len-1] = _str[end-1];
			end--;
		}
		strncpy(_str + pos, str, len);
		_size += len;
	}

	void erase(size_t pos, size_t len = npos)
	{
		if (len==npos || pos + len >= _size)
		{
			//有多少,删多少
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			size_t begin = pos + len;
			while (begin <= _size)
			{
				_str[begin - len] = _str[begin];
				begin++;
			}
			_size -= len;
		}
	}
	bool operator<(const string& s)const
	{

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

-1];
end–;
}
strncpy(_str + pos, str, len);
_size += len;
}

	void erase(size_t pos, size_t len = npos)
	{
		if (len==npos || pos + len >= _size)
		{
			//有多少,删多少
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			size_t begin = pos + len;
			while (begin <= _size)
			{
				_str[begin - len] = _str[begin];
				begin++;
			}
			_size -= len;
		}
	}
	bool operator<(const string& s)const
	{

[外链图片转存中…(img-5F8Ccsuv-1715598551661)]
[外链图片转存中…(img-JROIJsIo-1715598551661)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值