如果你能帮我澄清一些困惑的话,我会很高兴的。我正在编写一个函数,该函数删除字符串中的重复字符。AB-> "AAABB“。
void remove_dups(std::string& str) {
std::string::iterator it = str.begin();
while (it != str.end()) {
if (*(it+1) == *it) {
str.erase(it+1);
} else {
++it;
}
}
} 当我测试它的时候,它看起来很有效。但是,我在想,难道不应该有一个事后的问题吗?当'it‘是字符串的结尾时,if语句将查看不存在的下一个字符。据cplusplus.com称,
过去的结束字符是一个理论字符,它将跟随字符串中的最后一个字符。它不应被取消引用。 (http://www.cplusplus.com/reference/string/string/end/)
所以,我想知道为什么我的函数似乎通过了测试,以及如何以一种优雅的方式重写整个事件,以避开fencepost问题。(放松点,伙计们.)我是n00b。)
重写它就像
void remove_dups(std::string& str) {
std::string::iterator it = str.begin();
while (it != str.end()) {
if ((it+1) != str.end() && *(it+1) == *it) {
str.erase(it+1);
} else {
++it;
}
}
} 似乎不雅。
发布于 2014-01-11 08:46:26
所以我想知道为什么我的函数似乎通过了测试
不明确的行为并不意味着它不会做你想做的事。它可能会做你想做的事,只是出于错误的原因。我猜想,未定义的行为显示为获取\0字符,它不太可能等同于字符串中的其他字符。
以及如何以一种优雅的方式重写整件事,以避开fencepost问题。
有很多选择,但我个人的偏好是
if (it != str.end()) {
++it;
while (it != str.end()) {
/* compare *it and *(it-1) */
}
}但是请注意,erase必须移动元素。假设您总共删除了20个字符。您将将字符串的其余部分(一个字符)移回20次。如果稍微修改算法,则不需要这样做:
void remove_dups(std::string& str) {
std::string::iterator src = str.begin();
std::string::iterator dst = str.begin();
if (src != str.end()) {
++src;
++dst;
while (src != str.end()) {
if (*src != *(src-1)) {
*dst = *src;
++dst;
}
++src;
}
str.resize(dst - str.begin());
}
}这种方法的好处还在于,即使erase在内存中移动字符串,也能工作,这可能会导致迭代器中断。
发布于 2014-01-11 08:41:01
标准允许与过去的数组元素进行比较,不允许取消引用它。因此,您可以比较其地址,以验证循环是否应该结束,但不允许读取内容。由于迭代器大多是指针,所以这个规则也适用于字符串、向量、.
另外,请注意字符串的非序列版本::擦除返回迭代器到占用已删除元素的字符,您可以使用它作为新的循环迭代器。
发布于 2014-01-11 08:38:45
我认为最干净的解决方案是在字符串的末尾停止(字符串的最后一个字符),因为在这一点上,无论如何都不会执行进一步的操作。
因此,while (it != str.end())应该是while (it != str.end() && it != (str.end() - 1))。
https://stackoverflow.com/questions/21060256
复制相似问题