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

C++17 STL Стандартная библиотека шаблонов читать книгу онлайн
С++ — объектно-ориентированный язык программирования, без которого сегодня немыслима промышленная разработка ПО. В этой замечательной книге описана работа с контейнерами, алгоритмами, вспомогательными классами, лямбда-выражениями и другими интересными инструментами, которыми богат современный С++. Освоив материал, вы сможете коренным образом пересмотреть привычный подход к программированию.
Преимущество издания — в подробном описании стандартной библиотеки шаблонов С++, STL. Ее свежая версия была выпущена в 2017 году. В книге вы найдете более 90 максимально реалистичных примеров, которые демонстрируют всю мощь STL. Многие из них станут базовыми кирпичиками для решения более универсальных задач.
Вооружившись этой книгой, вы сможете эффективно использовать С++17 для создания высококачественного и высокопроизводительного ПО, применимого в различных отраслях.
Удаляем пробелы из начала и конца строк
Полученные из пользовательского ввода строки зачастую содержат лишние пробелы. В одном из предыдущих примеров мы удаляли повторяющиеся пробелы между словами.
Взглянем на пробелы, расположенные вокруг строк, и удалим их. Класс std::string содержит удобные вспомогательные функции, которые нам помогут.
После рассмотрения данного примера, в котором показано, как работать с простыми строковыми объектами, взгляните также и на следующий пример. Там вы увидите, как избежать ненужного копирования или изменения данных при работе с классом std::string_view.
Как это делается
В этом примере мы напишем вспомогательную функцию, которая определяет наличие пробелов в начале и конце строки и возвращает ее копию, но уже без таких пробелов. Затем немного протестируем ее.
1. Как и обычно, сначала идут заголовочные файлы и директива using:
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
using namespace std;
2. Наша функция будет принимать константную ссылку на существующую строку. А возвращать станет новую строку, из начала и конца которой удалены лишние пробелы:
string trim_whitespace_surrounding(const string &s)
{
3. Класс std::string предоставляет две удобные функции, которые будут очень полезны. Первая функция — это string::find_first_not_of, она принимает строку, содержащую все символы, которые мы хотим опустить. В нашем случае таковыми являются символы пробела ' ', табуляции 't' и перехода на новую строку 'n'. Функция возвращает позицию первого символа, не совпадающего с переданными. При наличии в строке только пробелов она вернет значение string::npos. Это значит следующее: если мы удалим все пробелы, то останется только пустая строка. Так что в подобных случаях просто возвращайте пустую строку:
const char whitespace[] {" tn"};
const size_t first (s.find_first_not_of(whitespace));
if (string::npos == first) { return {}; }
4. Теперь мы знаем, где должна начинаться новая строка, но пока неизвестно, где она заканчивается. Поэтому воспользуемся другой полезной функцией строки: string::find_last_not_of. Она вернет позицию последнего символа, не являющегося пробелом.
const size_t last (s.find_last_not_of(whitespace));
5. С помощью функции string::substr мы теперь можем вернуть часть строки, окруженной пробелами, в которой их не будет. Эта функция принимает два параметра: позицию от начала строки и количество символов после данной позиции.
return s.substr(first, (last - first + 1));
}
6. На этом все. Напишем функцию main, в которой создадим строку, окружающую предложение всеми видами пробелов, чтобы обрезать ее:
int main()
{
string s {" tn string surrounded by ugly"
" whitespace tn "};
7. Выведем на экран необрезанную и обрезанную версии строки. Окружив строку скобками, можно показать все пробелы, которые находились в ней до обрезки.
cout << "{" << s << "}n";
cout << "{"
<< trim_whitespace_surrounding(s)
<< "}n";
}
8. Компиляция и запуск программы дадут ожидаемый результат:
$ ./trim_whitespace
{
string surrounded by ugly whitespace
}
{string surrounded by ugly whitespace}
Как это работает
В этом разделе мы применили функции string::find_first_not_of и string::find_ last_not_of. Обе принимают строку, созданную в стиле C, в виде списка символов, которые нужно проигнорировать при поиске другого символа. Если у нас есть экземпляр строки, содержащий строку "foo bar", и мы вызовем для него функцию find_first_not_of("bfo "), то она вернет значение 5, поскольку символ 'a' — первый символ, который не входит в строку "bfo". Порядок символов в строке-аргументе неважен.
Существуют подобные функции с инвертированной логикой, однако мы не использовали их в этом примере:
string::find_first_of and string::find_last_of
По аналогии с функциями, основанными на итераторах, нужно проверять, возвращают ли эти функции реальную позицию в строке или же значение, которое указывает, что позиция символа, отвечающего условию, не была найдена. Если такой позиции нет, то они возвращают значение string::npos.
На основе позиций символов, полученных из этих функций, мы в рамках вспомогательной функции создаем подстроку, не содержащую пробелов в начале и конце, с помощью функции string::substring. Она принимает относительное смещение и длину строки, а затем возвращает новый экземпляр строки, для которого выделен собственный фрагмент памяти, содержащий только эту подстроку. Например, вызов string{"abcdef"}.substr(2, 2) вернет новую строку "cd".
Преимущества использования std::string без затрат на создание объектов std::string
Класс std::string очень полезен, поскольку значительно упрощает работу со строками. Его недостаток заключается в том, что при необходимости передать подстроку нужно передавать указатель и переменную, содержащую длину подстроки, два итератора или копию подстроки. Мы делали это в предыдущем примере, когда удаляли лишние пробелы из строки, вернув копию подстроки, которая не содержит их.
Если мы хотим передать строку или подстроку в библиотеку, которая не предоставляет поддержку класса std::string, то можем передать только необработанный указатель на строку, что несколько разочаровывает, поскольку этот способ использовался еще во времена С. Как и в случае с выделением подстроки, необработанный указатель не несет информации о длине строки. Таким образом, кто-то должен будет реализовать связку указателя и длины строки.
Говоря упрощенно, такой конструкцией как раз и является класс std::string_view. Он доступен, начиная с версии C++17, и предоставляет способ объединения указателя на некую строку и ее размера. Он воплощает идею наличия ссылочного типа для массивов данных.
Представим, что разрабатываем функции, которые ранее в качестве параметров принимали объекты типа std::string, но при этом не изменяли их так, чтобы экземплярам класса string потребовалось повторно выделять память, содержащую реальные данные. Мы могли бы использовать тип std::string_view для повышения совместимости с библиотеками, которые не знают об STL. Можно позволить другим библиотекам предоставлять string_view для строк, содержащих полезные данные, скрытые за сложной реализацией типа string, а затем применять их в нашем коде, совместимом с STL. Таким образом, класс string_view ведет себя как минималистичный и полезный интерфейс, пригодный для использования многими библиотеками.
Еще одной приятной особенностью является