Перейти к содержимому
Теория · Строки и массивы · урок 1 из 6

std::string и std::string_view

Тип char хранит ровно один символ. Имя, слово или фраза состоят из многих символов, и сколько их будет, заранее неизвестно. Такой текст хранят в строке (string) — переменной типа std::string из заголовка (header) <string>:

C++
#include <iostream>
#include <string>

int main() {
    std::string name = "Ada";
    std::string greeting = "Hello, " + name;
    std::cout << greeting << ' ' << greeting.size() << '\n';  // Hello, Ada 10
    std::cout << greeting[0] << greeting[7] << '\n';  // HA
    std::cout << (name == "Ada") << (name < "Bob") << '\n';  // 11
}

Оператор + склеил текст в кавычках со строкой name, size() вернул число символов, а greeting[7] — символ с номером 7. Сравнение name == "Ada" истинно, и std::cout печатает его как 1. Строки сравниваются посимвольно: решает первая пара разных символов, а если такой пары нет, меньше более короткая строка. У "Ada" и "Bob" это 'A' и 'B'.

Строка владеет символами

std::string хранит свои символы сам и при необходимости выделяет под них память. Копия строки получает собственные символы, поэтому изменение копии не трогает оригинал. Символы нумеруются с нуля, и последний стоит под номером size() - 1.

size() возвращает беззнаковое число типа std::size_t. Счётчик цикла по номерам символов объявляют того же типа. С int i сравнение i < word.size() вызывает предупреждение компилятора (compiler) с флагами -Wall -Wextra, как в задачах курса.

C++
std::string word = "code";
for (std::size_t i = 0; i < word.size(); ++i) {
    std::cout << i << word[i] << ' ';  // 0c 1o 2d 3e
}

Если номера не нужны, подойдёт цикл по диапазону (range-for): for (char c : word) по очереди кладёт в c каждый символ строки.

Представление строки

Функции, которая только читает текст, своя копия не нужна. Для таких параметров (parameters) есть тип std::string_view из заголовка <string_view>. Это представление (view): оно хранит адрес первого символа и длину, но не сами символы. Копировать представление дёшево, поэтому параметр std::string_view передают по значению.

C++
#include <iostream>
#include <string>
#include <string_view>

void print_length(std::string_view text) {
    std::cout << text << ' ' << text.size() << '\n';
}

int main() {
    std::string name = "Ada";
    print_length(name);       // Ada 3
    print_length("Babbage");  // Babbage 7
}

Параметр std::string_view принимает и std::string, и текст в двойных кавычках, ничего не копируя. Параметру const std::string& для "Babbage" пришлось бы сначала создать временную строку. Символы представления читают так же, как символы строки: через size(), [] и цикл по диапазону, а изменить их нельзя.

Пользоваться представлением можно, только пока жива строка, на которую оно смотрит. В отличие от const-ссылки (reference), представление не продлевает жизнь временной строки.

Частая ошибка

Представление создают из временной строки:

C++
std::string first = "Ada";
std::string_view full = first + " Lovelace";  // ✗
std::cout << full << '\n';
TEXT
warning: object backing the pointer will be destroyed at the end of the full-expression [-Wdangling-gsl]

Clang предупреждает, но программа собирается, и не каждый компилятор замечает такую ошибку. first + " Lovelace" создаёт временную строку, и она уничтожается сразу после объявления (declaration) full. Представление продолжает смотреть на её символы, то есть становится висячим (dangling), и чтение full — неопределённое поведение (undefined behavior).

То же случится с представлением строки, которую функция вернула по значению: такая строка тоже временная. Чтобы сохранить результат, объявите full как std::string.