Введение: две строки, одна головная боль
Каждый C++ разработчик рано или поздно сталкивается с дилеммой: std::string или std::wstring? Вроде бы оба — строки, оба из STL, но ведут себя совершенно по-разному. Особенно остро этот вопрос встаёт при работе с многоязычным текстом, эмодзи или иероглифами.
Если вы пишете на C++ и хотите, чтобы ваша программа корректно обрабатывала «Привет, мир!» на русском, японском или арабском — без понимания разницы между std::string и std::wstring не обойтись. В этой статье мы разберём оба подхода, сравним их по ключевым параметрам и решим, когда какой использовать.
Этот материал основан на реальном вопросе с Stack Overflow, собравшем тысячи просмотров. Мы адаптировали его для образовательных целей, добавив примеры и чёткую структуру.
std::string: классика и простота
std::string — это псевдоним для std::basic_string<char>. Каждый символ занимает 1 байт. Исторически сложилось, что char использовался для ASCII-символов, но сегодня он чаще всего хранит данные в кодировке UTF-8 (или просто байты).
Плюсы std::string
- Экономия памяти — 1 байт на символ для ASCII/UTF-8.
- Совместимость — работает с большинством библиотек, включая C-функции (
strlen,strcpy). - Скорость — меньше накладных расходов на аллокацию.
- Повсеместность — стандарт де-факто для передачи данных по сети и в JSON.
Минусы std::string
- Путаница с кодировками —
std::stringне знает, в какой кодировке хранятся данные. Вы можете хранить в нём UTF-8, но.size()вернёт количество байт, а не символов. - Проблемы с Unicode — итерация по строке может разбить многобайтовый символ.
- Нет встроенной поддержки широких символов — для работы с Unicode вне UTF-8 придётся подключать сторонние библиотеки.
// Пример: std::string с UTF-8
#include <iostream>
#include <string>
int main() {
std::string s = "Привет, мир!";
std::cout << "Длина в байтах: " << s.size() << std::endl; // 21 байт
std::cout << "Первый символ (как char): " << s[0] << std::endl; // Мусор: первый байт UTF-8
return 0;
}
В этом примере s[0] вернёт не 'П', а первый байт двухбайтового символа 'П' (0xD0). Это частая ошибка новичков.
std::wstring: мощь широких символов
std::wstring — это std::basic_string<wchar_t>. Размер wchar_t зависит от платформы: 2 байта на Windows (UTF-16) и 4 байта на Linux/macOS (UTF-32).
Плюсы std::wstring
- Фиксированный размер символа (на конкретной платформе) — упрощает работу с Unicode.
- Прямая поддержка локалей — в Windows все WinAPI-функции работают с
wchar_t. - Меньше проблем с многобайтовыми символами — каждый символ — это один элемент строки.
Минусы std::wstring
- Размер зависит от платформы — код может вести себя по-разному на Windows и Linux.
- Больший расход памяти — 2 или 4 байта на символ, даже для ASCII.
- Проблемы с совместимостью — не все библиотеки поддерживают
wchar_t. - Суррогатные пары — в UTF-16 некоторые символы (например, эмодзи) занимают 2
wchar_t, что снова создаёт проблему «символ ≠ элемент».
// Пример: std::wstring на Windows
#include <iostream>
#include <string>
int main() {
std::wstring ws = L"Привет, мир!";
std::wcout << L"Длина: " << ws.size() << std::endl; // 12 символов (на Windows)
std::wcout << L"Первый символ: " << ws[0] << std::endl; // 'П' (корректно)
return 0;
}
Обратите внимание: на Windows ws.size() вернёт 12 (количество символов), а не 21, как в случае с std::string. Но на Linux wchar_t занимает 4 байта, и размер строки может отличаться.
Сравнение в таблице
| Характеристика | std::string | std::wstring |
|---|---|---|
| Тип символа | char (1 байт) | wchar_t (2 или 4 байта) |
| Кодировка по умолчанию | Не определена (часто UTF-8) | UTF-16 (Windows) / UTF-32 (Linux) |
| Размер в памяти (ASCII) | 1 байт/символ | 2 или 4 байта/символ |
| Поддержка Unicode | Требует внешней обработки | Встроенная (но с нюансами) |
| Переносимость | Высокая | Средняя (зависит от платформы) |
| Использование в WinAPI | Требует конвертации | Напрямую |
Когда что выбирать?
Выбирайте std::string, если:
- Вы работаете с кроссплатформенным кодом.
- Вам нужна минимальная память и