vector迭代器的使用切不可真正当指针使,尽管底层会调用其偏特化版本,但是它自身调用是基于一个有用的前提:这个指针本身是有效的,即指向地址合法。
现在有个应用:
请编写一个方法,将字符串中的空格全部替换为“%20”。假定该字符串有足够的空间存放新增的字符,并且知道字符串的真实长度(小于等于1000),同时保证字符串由大小写的英文字母组成。
给定一个string iniString 为原始的串,以及串的长度 int len, 返回替换后的string。
测试样例:
“Mr John Smith”,13
返回:”Mr%20John%20Smith”
”Hello World”,12
返回:”Hello%20%20World”
于是我写了下面的代码:
class Replacement {
public:
string replaceSpace(string iniString, int length) {
//
/*****************错误发生在下面***********/
string::iterator front = iniString.end()-1;
int c = count(iniString.begin(), iniString.end(), ' ');
iniString.resize(length+c*2, ' ');
/***************错误发生在上面*************/
string::iterator back = --(iniString.end());
while(iniString.begin() != front) {
if (*front != ' ') {
*back = *front;
--back; --front;
}
else {
*back = '0'; --back;
*back = '2'; --back;
*back = '%'; --back;
--front;
}
}
return iniString;
}
};
上面可以对迭代器直接+n,因为这个迭代器类型是RandomAcessIterator,所以合法。注意到注释部分,错误就发生在那一部分:段错误,为什么?迭代器失效了!!!
我们知道string的空间也是动态增长的,这个和vector类似,并不是在原有的空间末尾再续空间,而是开辟一个新的、原空间两倍大的内存区域,把原始数据拷贝过来后释放原来的空间。对这个有了了解,重点再看以下句子:
string::iterator front = iniString.end()-1;
int c = count(iniString.begin(), iniString.end(), ' ');
iniString.resize(length+c*2, ' ');
它的意思就是先找到原来string的最后一个字符,然后把这个string空间扩大,用来把空格替换为“20%”,很好理解。但是第一句话掉入一个陷阱:这个front在resize()之后,并不是我们想要的那最后一个字符的位置,它可能是任何东西,就是不是想要的,原因已经说过了。
那么如何避免这种错误?这只是一个习惯性的约束,因为在很多场合以上写法是合理的,比如list、map、set之类的容器,那么习惯上要保持一致才不容易犯错误,事实上,只需要将有可能造成容器变化的语句放置在迭代器定义之前就好,就像下面:
int c = count(iniString.begin(), iniString.end(), ' ');
iniString.resize(length+c*2, ' ');
string::iterator front = iniString.begin()+length-1;
保持一种习惯,每每碰到问题就会调节反射一般问自己:迭代器会失效吗?