C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

C++17 STL Стандартная библиотека шаблонов читать книгу онлайн
С++ — объектно-ориентированный язык программирования, без которого сегодня немыслима промышленная разработка ПО. В этой замечательной книге описана работа с контейнерами, алгоритмами, вспомогательными классами, лямбда-выражениями и другими интересными инструментами, которыми богат современный С++. Освоив материал, вы сможете коренным образом пересмотреть привычный подход к программированию.
Преимущество издания — в подробном описании стандартной библиотеки шаблонов С++, STL. Ее свежая версия была выпущена в 2017 году. В книге вы найдете более 90 максимально реалистичных примеров, которые демонстрируют всю мощь STL. Многие из них станут базовыми кирпичиками для решения более универсальных задач.
Вооружившись этой книгой, вы сможете эффективно использовать С++17 для создания высококачественного и высокопроизводительного ПО, применимого в различных отраслях.
Благодаря выходу 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, поскольку собираемся создать строку вместо