std::string и std::string_view в C++: разница, производительность и безопасные паттерны
Запрос «string_view в C++: когда использовать и чем он лучше std::string» — один из самых частых у начинающих. Разобраться важно: от правильного выбора типа зависит и производительность, и безопасность кода. В этой статье мы сравним std::string и std::string_view, разберёмся с владением, временем жизни, покажем практичные примеры и типичные ошибки.
Ключевая идея: владение против представления
- std::string — владеет буфером (динамическая память), отвечает за выделение/освобождение, изменяемый.
- std::string_view — невладеющее представление (указатель + длина). Ничего не копирует и не освобождает. Только «смотрит» на чужие данные.
Из этого следует правило: если функция только читает строку, удобнее принимать std::string_view. Если нужно хранить, модифицировать или продлить жизнь данных — используйте std::string.
Создание и совместимость
std::string_view легко строится из строковых литералов, std::string и C‑строк:
#include <string>
#include <string_view>
void f(std::string_view sv);
int main() {
std::string s = "hello";
const char* c = "world";
f(s); // из std::string
f(c); // из C-строки
f("literal"); // из литерала
}
Обратное преобразование (view → string) требует копирования:
std::string_view sv = "data";
std::string owned = std::string(sv); // копия, владение теперь у owned
Где string_view особенно полезен
1) Входные параметры без копий
#include <string_view>
#include <cctype>
// Подсчёт слов: разделитель — пробельный символ
size_t count_words(std::string_view text) {
size_t count = 0;
bool in_word = false;
for (unsigned char ch : text) {
if (std::isspace(ch)) {
in_word = false;
} else if (!in_word) {
in_word = true;
++count;
}
}
return count;
}
Функция принимает и std::string, и литералы, и C-строки без лишних копий.
2) Быстрый trim без аллокаций
#include <string_view>
#include <cctype>
std::string_view trim(std::string_view sv) {
auto is_space = [](unsigned char c){ return std::isspace(c); };
while (!sv.empty() && is_space(sv.front())) sv.remove_prefix(1);
while (!sv.empty() && is_space(sv.back())) sv.remove_suffix(1);
return sv; // всё ещё view на исходные данные
}
3) Лёгкий сплит по разделителю
#include <string_view>
#include <vector>
std::vector<std::string_view> split(std::string_view sv, char delim) {
std::vector<std::string_view> parts;
size_t start = 0;
while (true) {
size_t pos = sv.find(delim, start);
if (pos == std::string_view::npos) {
parts.push_back(sv.substr(start));
break;
}
parts.push_back(sv.substr(start, pos - start));
start = pos + 1;
}
return parts; // вектора view; копий строк нет
}
Важно: полученные подстроки живут не дольше исходных данных, на которые они ссылаются.
Опасные места и как их избежать
1) Возврат view на временные данные — ошибка
// ПЛОХО: возвращаем view на локальный std::string
std::string_view bad() {
std::string s = "temp";
return s; // висячая ссылка (dangling)
}
Правильно: вернуть std::string (владеющий) или принять буфер снаружи.
// Хорошо: возвращаем владение
std::string good() {
std::string s = "ok";
return s; // RVO/NRVO, без лишней копии
}
2) Долгое хранение string_view
std::string make()
{
std::string s = "hello";
return s;
}
std::string_view sv;
{
std::string tmp = make();
sv = tmp; // sv указывает внутрь tmp
} // tmp разрушен — sv теперь висячий
Решение: если нужно хранить — храните std::string или делайте копию:
std::string owned;
{
std::string tmp = make();
owned = tmp; // корректно
}
3) Реаллокиация ломает все view
std::string s = "abc";
std::string_view v = s; // указывает внутрь s
s += "def"; // возможна реаллокация буфера
// v может стать недействительным
Вывод: не храните string_view на строку, которую планируется изменять (push_back, append, resize и т.п.).
Производительность: когда реально быстрее
- Создание/копирование std::string_view — O(1).
- Подстроки через substr у view — O(1), без копирования данных.
- Копирование std::string — O(N), выделение памяти, перенос байтов.
Поэтому в API для чтения данных используйте string_view. В местах, где нужны собственные данные или модификация — string.
Интероп с C‑API: c_str() и data()
- std::string::c_str() и data() в C++17 возвращают нуль-терминированный буфер (const char*).
- std::string_view::data() — указатель на первый символ без гарантии завершающего нуля.
extern "C" void c_api(const char*);
std::string s = "hello";
c_api(s.c_str()); // ок
std::string_view sv = "world";
// c_api(sv.data()); // может не содержать '\0' в конце — небезопасно
// Решение: скопировать
std::string tmp(sv);
c_api(tmp.c_str());
Практика: парсинг ключ=значение без копий
#include <string_view>
#include <optional>
#include <utility>
std::optional<std::pair<std::string_view, std::string_view>>
parse_kv(std::string_view sv) {
auto pos = sv.find('=');
if (pos == std::string_view::npos) return std::nullopt;
auto key = sv.substr(0, pos);
auto val = sv.substr(pos + 1);
return std::make_pair(trim(key), trim(val));
}
Функция возвращает пары view; при необходимости сохранения значений — скопируйте их в std::string.
Частые вопросы
Можно ли модифицировать через string_view? Нет. Это read-only представление. Если нужно изменить — используйте string или span к изменяемому буферу.
Как быть с Unicode? std::string — это просто байты. Операции size(), substr() работают по байтам, а не по кодовым точкам. Для Unicode учитывайте кодировку (UTF‑8) и используйте специализированные библиотеки.
Рекомендации и паттерны
- В публичном API для входных строк — std::string_view.
- Не храните string_view дольше жизни источника; не используйте его на изменяемую строку.
- Нужна долговременная копия — явный std::string.
- Для C‑API используйте c_str() у std::string; у string_view при необходимости делайте копию.
- Профилируйте: string_view ускоряет код, убирая копии. Но не жертвуйте безопасностью времени жизни.
Хотите системно прокачать основы C++ (типы, ссылки, строки, память, ООП) и практику на проектах? Загляните в пошаговый курс «C++ с Нуля до Гуру» — от синтаксиса к реальным задачам.
Итоги
std::string — для владения и модификации. std::string_view — для чтения без копий. Соблюдайте правила времени жизни и не храните view «на будущее». С этими принципами ваш код станет одновременно быстрее и надёжнее.
-
Создано 22.12.2025 17:01:03
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.