У меня есть следующий код, который принимает строку и стирает не алфавитные символы
void removeNonAlpha(string& str){
for (string::iterator it = str.begin(); it < str.end(); it++){
if (!(isUpperCaseLetter(*it) || isLowerCaseLetter(*it) || str == ' '))
str.erase(it--);
}
}
Я показал это своему профессору, и он сказал мне, что делать это рискованно, потому что это может сделать недействительным используемый мной итератор. Тем не менее, я думал, что стирание только сделает недействительными итераторы после точки стирания, и я позаботился о том, чтобы не использовать никаких итераторов после этой точки.
Так может ли этот код вылетать или вызывать неопределенное поведение?
std::vector::erase
работает так, как вы предлагаете; он делает недействительными только итераторы, начиная с первого стертого элемента. Однако это не относится к std::string
,
Стандарт C ++ традиционно был более гибким с требованиями к std::string
, (Или, другими словами, он традиционно позволял разработчикам использовать оптимизации, которые не были бы действительны для векторов.) std::string::erase
и другие строковые мутаторы.
В [string.require]
(§21.4.1 из n3797), стандарт допускает, что:
basic_string
последовательность может быть признана недействительной из-за следующих basic_string
объект:
basic_string
в качестве аргумента.operator[]
, at
, front
, back
, begin
, rbegin
, end
, а также rend
,Другими словами, вызов потенциально мутирующей функции, такой как std::string::erase
может сделать недействительными все итераторы для этой строки, даже если в строку не внесены видимые изменения (например, потому что диапазон, который будет удален, пуст).
(Последний проект стандарта C ++ имеет ту же формулировку, хотя теперь это параграф 4.)
В первом цикле через строку, итератор it
имеет значение str.begin()
, Этот итератор не может быть уменьшен, поскольку результат не будет находиться внутри строки. И, следовательно, увеличение уменьшенного итератора может не вернуть it
в str.begin()
для следующей итерации.
Ничто из вышеперечисленного не относится к целочисленным индексам позиции. Так что, если бы вы могли смело заменить ваш цикл очень похожим:
void removeNonAlpha(string& str){
for (auto sz = str.size(), i = 0; i < sz; ++i){
if (!(isUpperCaseLetter(str[i]) ||
isLowerCaseLetter(str[i]) ||
str[i] == ' '))
str.erase(i--);
}
}
Это не скомпилируется. str
это std :: string и str == ' '
два разных типа.
Во-вторых, при повторной итерации необходимо использовать string :: reverse_iterator, string :: rbegin () и string :: rend ();