迭代器失效的例子

来源:互联网 发布:为知笔记 知乎 编辑:程序博客网 时间:2024/04/27 19:38

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;

保持一种习惯,每每碰到问题就会调节反射一般问自己:迭代器会失效吗?

0 0