std::string: текст, который управляет собой сам
На предыдущей странице вы видели, как умные указатели позволяют объекту владеть ресурсом и очищать его автоматически. std::string - та же идея, применённая к тексту: он владеет буфером символов, увеличивает его при добавлении и освобождает, когда строка выходит из области видимости. Вы никогда не вызываете new, никогда не считаете байты и никогда не переживаете о пропущенном нулевом завершителе.
Чтобы использовать его, подключите <string>. Класс находится в пространстве имён std.
Этот оператор + выполняет настоящую работу - он выделяет новый буфер, достаточно большой для обеих частей, и копирует их в него. С сырым char* вам пришлось бы тянуться к strcpy и strcat и надеяться, что буфер достаточно велик. std::string заставляет весь этот класс ошибок исчезнуть.
Сборка и объединение строк
Конкатенировать можно с помощью +, но рабочая лошадка для наращивания строки на месте - это +=. Он дописывает в существующий буфер вместо того, чтобы каждый раз порождать совершенно новую строку, что важно внутри циклов.
Частая неожиданность: + требует, чтобы хотя бы один операнд уже был std::string. Два строковых литерала - это просто const char*, поэтому "a" + "b" не компилируется: для двух сырых указателей нет operator+. Сначала сделайте один из них строкой:
string s = "a" + "b"; // error: can't add two const char*
string s = string("a") + "b"; // fine - left side is a std::string
string s = "a"s + "b"; // fine in C++14+, the "s" literal suffix
Обратите внимание, что последняя форма использует суффикс s из <string> (using namespace std::string_literals;), который превращает литерал напрямую в std::string.
Доступ к содержимому строки
std::string ведёт себя как контейнер из char, поэтому его можно индексировать, перебирать в цикле и запрашивать у него фрагменты.
substr(pos, len) возвращает совершенно новую строку, скопированную из исходной; она не изменяет источник. Следите за границами: word[10] для строки из 5 символов - это неопределённое поведение: он ничего не бросит, просто прочитает мусор. Если нужен проверенный доступ, который бросает std::out_of_range, используйте word.at(10) вместо word[10].
Ещё одна классическая ловушка: .size() возвращает беззнаковый тип (size_t). Цикл назад вроде for (size_t i = word.size() - 1; i >= 0; --i) никогда не завершится, потому что беззнаковое i никогда не может опуститься ниже нуля - оно переполняется и превращается в огромное число. Используйте знаковый индекс или перестройте условие, когда нужно пройти строку в обратном порядке.
Поиск и замена
find находит подстроку или символ и возвращает начальный индекс. Когда цель отсутствует, он возвращает специальную константу std::string::npos - всегда сравнивайте с ней, а не предполагайте, что find возвращает -1.
Чтобы изменить текст на месте, replace(pos, len, text) заменяет участок новым содержимым (которое может быть другой длины), а insert/erase добавляют или удаляют фрагменты:
Строки и числа
Текст и числа не преобразуются сами собой - "42" это три символа, а не целое 42. Стандартная библиотека даёт вам функции преобразования в обе стороны. Используйте stoi, stod и им подобные, чтобы разбирать текст в числа, и to_string для движения в обратном направлении.
Здесь две ловушки. Во-первых, stoi("abc") бросает std::invalid_argument, поэтому защищайте недоверенный ввод с помощью try/catch. Во-вторых, to_string(3.99) даёт полное 3.990000 - если вам нужен контроль над точностью и форматированием, это задача для строковых потоков (string streams), и именно туда мы направимся дальше.
try {
int n = std::stoi(userInput);
} catch (const std::invalid_argument&) {
std::cout << "That wasn't a number.\n";
}
Далее: ввод и вывод
Всё это время вы выводили строки с помощью cout, но их чтение от пользователя таит свои сюрпризы - cin >> name останавливается на первом пробеле, поэтому для целой строки вроде "Ada Lovelace" вместо этого нужен std::getline. Следующая страница как следует разбирает ввод и вывод в C++: потоковые операторы, чтение целых строк и сочетание >> с getline без спотыкания об оставшиеся символы новой строки.
Часто задаваемые вопросы
В чём разница между std::string и char* в C++?
std::string - это класс, который владеет собственной памятью и управляет ею, растёт по мере необходимости и очищается автоматически. char* - это всего лишь сырой указатель на символы с завершающим '\0': буфер, длину и время жизни вы отслеживаете сами. Для почти всего предпочитайте std::string; он устраняет целые классы ошибок переполнения буфера и висячих указателей.
Как получить длину строки в C++?
Вызовите .size() или её псевдоним .length() у std::string: name.size() возвращает количество символов как size_t. Оба возвращают одно и то же значение; size() более распространённый стиль, потому что он соответствует всем остальным контейнерам STL.
Как преобразовать строку в число в C++?
Используйте std::stoi для int, std::stod для double и подобные (stol, stof). Пример: int n = std::stoi("42");. Эти функции бросают std::invalid_argument, если текст не является числом, поэтому оборачивайте их в try/catch, когда ввод не вызывает доверия.