C++知识点20——使用C++标准库(再谈string——string的大小、容量、交换与迭代器失效)

1.string的大小与容量

size_t size() const;
bool empty() const;
void resize (size_t n);
void resize (size_t n, char c);
void reserve (size_t n = 0);//默认实参是0,如果不传参,和shrink_to_fit的效果相同
void shrink_to_fit();

string中的reserve可以缩减内存,而vector中的reserve只能预分配内存

void reservestring()
{
	string s="1234567890";
	s.reserve(100);
	cout<<s<<','<<s.capacity()<<endl;
	s.reserve();
	cout<<s<<','<<s.capacity()<<endl;
}

在调用reserve后,string的capacity变小了,效果同shrink_to_fit

其余的同vector,见博客https://blog.csdn.net/Master_Cui/article/details/107503634

 

2.交换两个string

void swap (string& str);
void swap (string& x, string& y);

和vector几乎相同,见博客https://blog.csdn.net/Master_Cui/article/details/107427911

但是使用swap时,有可能导致迭代器失效

 

3.string的迭代器失效

string和vector迭代器失效的情况类似但是并不完全一样,关于vector迭代器失效讲解见博客https://blog.csdn.net/Master_Cui/article/details/107503634

string和vector分配内存的情况几乎是一样的,但是string在分配内存时是有优化的,这一点导致string和vector迭代器失效的情况类似但是并不完全一样

在Ubuntu18.04下,string对象存在SSO(短字符串优化策略),即,当字符串的长度小于等于15时,字符串本身所占的内存被分配在栈内存上,如果字符串长度大于15,那么字符串本身所占的内存会被分配在堆内存上

SSO优化的原因:C++的std::string都是存储在heap中,导致访问std::string需要经过一次寻址过程,速度较慢。所以,很多string的字符串长度很小时,把字符串存储到栈上,从而快速分配内存,优化创建速度,并且访问栈上数据的局部性很好,速度比较快。

所以如果swap的两个字符串都是长度大于15的,那么不会产生迭代器失效,两个字符串任何一个的长度小于15,那么就会产生迭代器失效的问题

 

示例

void iteratorfailed2()
{
	string s1="1234567890qwertyuiop";
	string s2="1234567890";

	string::iterator it1=s1.begin()+10;
	string::iterator it2=s2.begin()+3;

	cout<<*it1<<" "<<*it2<<endl;
	swap(s1,s2);

	cout<<s1<<endl<<s2<<endl;
	cout<<*it1<<" "<<*it2<<endl;
}

可见,当swap后,it2指向的内存已经不是swap前s2所占的内存,所以没有输出正确的字符,原因就是s2的数据存储在栈内存上,交换后,原来的s2数据的栈内存被释放后又被重新分配

所以,当在Linux下做字符串的swap操作时,要注意字符串的长度是否大于15,如果小于等于15,会由迭代器失效的风险

 

除了swap之外,其他迭代器失效的情况和vector类似

1).string重新分配内存

void iteratorfailed()
{
	string s="1234567890";
	string s2="qwertyuio";
	string::iterator it = s.begin();
	cout<<*it<<endl;

	s.insert(s.size(), s2, 0, 8);
	cout<<s<<endl;
	cout<<*it<<endl;
}

 

2).保存了插入位置之后的迭代器

这种情况对于长度小于15的字符串,插入删除位置之后的迭代器依然有效

当字符创长度大于15时,string的数据的内存在堆内存上,堆内存连续,所以迭代器失效

void iteratorfailed3()
{
	string s="1234567890qwertyuiop";
	string::iterator it=s.begin()+3;
	cout<<*it<<endl;

	s.insert(s.begin(), 'c');
	cout<<s<<endl;
	cout<<*it<<endl;
}

 

当字符串长度小于15时,此时,string的数据的内存在栈内存上,栈内存连续,所以迭代器依然有效

void iteratorfailed2()
{
	string s="1234567890";
	string::iterator it=s.begin()+3;
	cout<<*it<<endl;

	s.insert(s.begin(), 'c');
	cout<<s<<endl;
	cout<<*it<<endl;
}

 

但是,无论字符串的长度大于还是小于15,当保存了删除位置之后的迭代器,进行删除操作后解引用该迭代器,该迭代器都是有效的,不知道为啥,可能和编译器有关,也可能和系统实现有关(如果有人知道为啥,请告诉博主)

void iteratorfailed4()
{
	string s="1234567890qwertyuiop";
	string::iterator it=s.begin()+7;
	cout<<*it<<endl;

	s.erase(s.begin()+2);
	cout<<s<<endl;
	cout<<*it<<endl;
}

 

void iteratorfailed4()
{
	string s="1234567890";
	string::iterator it=s.begin()+7;
	cout<<*it<<endl;

	s.erase(s.begin()+2);
	cout<<s<<endl;
	cout<<*it<<endl;
}

上述两种情况,删除位置之后的迭代器都是有效的,但是,在编写程序时,不要这么做

 

3).解引用了insert或erase后的迭代器

void iteratorfailed5()
{
	string s="1234567890qwertyuiop";
	string::iterator it = s.begin()+5;
	s.insert(s.begin(), *it);
	cout<<*it<<endl;
}

没有输出对应的元素,因为string的迭代器失效,解决办法是利用返回值更新it

 

在Linux下如果string对象的长度小于15,那么insert之后,迭代器依然有效,原因仍然和SSO有关

void iteratorfailed5()
{
	string s="1234567890";
	string::iterator it = s.begin()+5;
	s.insert(s.begin(), *it);
	cout<<*it<<endl;
}

 

erase同理

void iteratorfailed6()
{
	string s="1234567890qwertyuiop";
	string::iterator it = s.begin()+5;
	s.erase(*it);
	cout<<*it<<endl;
}

无论string的长度是否小于15,迭代器均失效

 

4).在执行删除操作时,逻辑不严谨,导致解引用了尾后迭代器

void iteratorfailed7()
{
	string s="123456789";
	string::iterator it=s.begin();
	while(it!=s.end()) {
		it=s.erase(it);
		++it;
		cout<<*it<<endl;
	}
}

解决办法见博客https://blog.csdn.net/Master_Cui/article/details/107503634

 

总结:

string的迭代器失效和vector类似也可以分为5种情况

1.向string对象中添加元素,导致string重新分配内存

2.在insert前,保存了insert位置之后的迭代器,在insert之后,解引用该迭代器(这种情况受SSO机制的影响),但是erase操作时,没有该问题,可能和系统实现有关

3.insert或erase之后,迭代器没有及时更新,导致迭代器失效

4.没有添加对应的逻辑判断,导致解引用了尾后迭代器

5.在Linux下,对于长度小于15的string对象,受SSO机制的影响,swap后,会导致长度小于15的string对象的迭代器失效

 

参考

《C++ Primer》

http://www.cplusplus.com/reference/string/string/string/

https://www.cnblogs.com/ll-10/p/9633968.html

https://blog.csdn.net/ThorKing01/article/details/97273850

https://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html

https://www.cnblogs.com/houjun/p/4909413.html

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值