chitay-knigi.com » Разная литература » C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 60 61 62 63 64 65 66 67 68 ... 121
Перейти на страницу:
утомительна, особенно при анализе, конкатенации и копировании и т.д., класс std::string — это реальный шаг вперед к простоте и безопасности.

Благодаря выходу C++11 теперь даже не нужно копировать строки, когда мы хотим передать право собственности какой-то другой функции или структуре данных, поскольку можем перемещать их. Таким образом, в подобных случаях не возникает больших издержек.

По мере выхода новых стандартов класс std::string получил несколько новых свойств. Совершенно новой особенностью С++17 является std::string_view. Мы немного поработаем с ней (но впереди будет и другой пример, в котором более подробно рассматривается std::string_view), чтобы понять, как взаимодействовать с подобным инструментарием в эпоху С++17.

Как это делается

В этом примере мы создадим строки и строковые представления, а затем выполним простые операции конкатенации и преобразования.

1. Как и обычно, сначала включим заголовочные файлы и объявим об использовании пространства имен std:

#include <iostream>

#include <string>

#include <string_view>

#include <sstream>

#include <algorithm>

using namespace std;

2. Сначала создадим объекты строк. Самым очевидным способом является инстанцирование объекта класса string. Мы контролируем его содержимое, передавая конструктору строку в стиле C (которая после компиляции будет встроена в бинарный файл как статический массив, содержащий символы). Конструктор скопирует ее и сделает содержимым объекта строки a. Помимо этого, вместо инициализации строки с помощью строки в стиле C можно применить оператор строкового литерала ""s. Он создает объект строк динамически. Мы используем его для создания объекта b, что позволит применять автоматическое выведение типа.

int main()

{

  string a { "a" };

  auto b ( "b"s );

3. Строки, которые мы только что создали, копируют их входные данные из аргумента конструктора в собственный буфер. Чтобы не копировать строку, а просто сослаться на нее, можно воспользоваться объектами типа string_ view. Этот класс также имеет оператор литерала, он вызывается с помощью конструкции ""sv:

  string_view c { "c" };

  auto d ( "d"sv );

4. Теперь поработаем с нашими строками и строковыми представлениями. Для обоих типов существует перегруженная версия оператора << для класса std::ostream, поэтому их удобно выводить на экран:

  cout << a << ", " << b << 'n';

  cout << c << ", " << d << 'n';

5. В классе string имеется перегруженная версия оператора +, поэтому можно сложить две строки и получить в качестве результата их конкатенацию. Таким образом, выражение "a" + "b" даст результат "ab". Конкатенация строк a и b с помощью данного способа выполняется довольно легко. При необходимости сложить a и c могут возникнуть некоторые трудности, поскольку c — не строка, а экземпляр класса string_view. Сначала нужно получить строку из c, это делается путем создания новой строки из c и сложения ее с a. Кто-то может задаться вопросом: «Погодите, зачем копировать с в промежуточную строку только для того, чтобы сложить ее с а? Можно ли этого избежать, использовав конструкцию c.data()?» Идея хороша, но имеет недостаток: экземпляры класса string_view не обязаны содержать строки, завершающиеся нулем. Данная проблема может привести к переполнению буфера.

  cout << a + b << 'n';

  cout << a + string{c} << 'n';

6. Создадим новую строку, содержащую все введенные нами строки и строковые представления. С помощью std::ostringstream можно поместить любую переменную в объект потока, который ведет себя точно так же, как и std::cout, но не выводит данные на консоль. Вместо этого он выводит данные в строковый буфер. После того, как мы поместим все переменные в поток, разделив их пробелами с помощью оператора <<, можем создать и вывести на экран новый объект строки, задействовав конструкцию o.str().

  ostringstream o;

  o << a << " " << b << " " << c << " " << d;

  auto concatenated (o.str());

  cout << concatenated << 'n';

7. Мы также можем преобразовать эту новую строку путем, например, перевода всех ее символов в верхний регистр. Библиотечная функция C toupper, которая соотносит символы в нижнем регистре и символы в верхнем, оставляя другие символы неизменными, уже доступна, и ее можно объединить с функцией std::transform, поскольку строка, по сути, представляет собой итерабельный контейнер, содержащий элементы типа char.

  transform(begin(concatenated), end(concatenated),

            begin(concatenated), ::toupper);

  cout << concatenated << 'n';

}

8. Компиляция и запуск программы дадут следующий результат, который полностью оправдывает наши ожидания:

$ ./creating_strings

a, b

c, d

ab

ac

a b c d

A B C D

Как это работает

Очевидно, строки можно складывать с помощью оператора + прямо как числа. Математика здесь не используется, но в итоге мы получаем сконкатенированные строки. Чтобы иметь возможность работать с объектами класса string_view, сначала нужно выполнить их преобразование к типу std::string.

Однако очень важно отметить: при объединении в коде строк и строковых представлений мы никогда не должны предполагать, что хранящаяся в экземпляре класса string_view строка завершается нулевым символом! Именно поэтому мы используем конструкцию "abc"s + string{some_string_view} вместо конструкции "abc"s + some_string_view.data(). Кроме того, класс std::string предоставляет функцию-член append, которая может работать с экземплярами класса string_view, но изменяет строку вместо того, чтобы вернуть новую строку, к которой прикреплено содержимое строкового представления.

 

 Класс std::string_view очень полезен, но будьте осторожны при смешивании его со строками и строковыми функциями. Нельзя рассчитывать на то, что эти строки будут оканчиваться нулями, поэтому весь порядок быстро нарушается в стандартном строковом окружении. К счастью, зачастую предусмотрены подходящие перегруженные версии функций, которые могут обрабатывать подобные ситуации.

Сложную конкатенацию строк с форматированием и т.д. не следует выполнять шаг за шагом для экземпляров строк. Классы std::stringstream, std::ostringstream и std::istringstream подходят для этого гораздо лучше, так как позволяют более качественно управлять памятью при объединении строк и предоставляют все возможности по форматированию, характерные для потоков. Для данного раздела мы выбрали класс std::ostringstream, поскольку собираемся создать строку вместо

1 ... 60 61 62 63 64 65 66 67 68 ... 121
Перейти на страницу:

Комментарии
Минимальная длина комментария - 25 символов.
Комментариев еще нет. Будьте первым.