Архив рубрики: Библиотеки C++

pow() — функция библиотеки cmath




pow(a, b);

Функция pow() библиотеки cmath принимает два параметра: a, b. Первое число a (базовое) возводится в степень b.

Возвращает значение ab .

Результат выполнения 23 , 53, 52 :

pow () - функция библиотеки cmath

 

Рассылка новых уроков по программированию:

fma () — функция библиотеки cmath




fma(a, b, c);

Функция fma() библиотеки cmath принимает три параметра: a, b — значения для умножения, c — значение для добавления.

Возвращает значение a * b + c.

Результат выполнения ( 2 * 2 + 3):

fma () - функция библиотеки cmath

Рассылка новых уроков по программированию:

Указатели в контейнерах. STL (часть 16)




указатели в контейнерах, STL, программирование для начинающих

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

Примечание: Степень выраженности таких эффектов зависит и от типа контейнера, и от рассматриваемых операций. Например, операции взаимного обмена 2-х элементов в ходе сортировки потребует 3-х копирований для контейнера типа vector и не потребует дополнительных копирований для контейнера list. Но операции начального помещения элемента в контейнер (push_back(), insert() и др.) всегда выполняются копированием.

Это может стать проблемой для приложения, когда операции на контейнерах критичны по времени выполнения (или кажутся вам таковыми). Есть и ещё более сложные случаи, когда для класса объектов контейнера не определена операция копирования, или когда объекты представляют собой сами ссылочные объекты, полное копирование для которых необходимо выполнять рекурсивными процедурами следования по всем ссылкам (то, что в языке Python и других называют глубоким копированием).

Всё работает благополучно, как и ранее — сортируем набор записей по произвольным полям и в любом порядке:

16-1

(Обратите внимание, что завершать это приложение нужно по Ctrl+D — End Of File … в Widows, наверное, по Ctrl+Z.)

Но! … Специально были оставлены отладочные «следы» срабатываний конструкторов и деструкторов записей, и записи конструируются 8 раз, а деструктор срабатывает только 4, для локального массива в точке выхода из блока. Локальный массив для нас вообще не представляет интереса. Он введен для упрощения примера только как набор инициализирующих значений. А вот для записей, помещаемых в контейнер, уничтожение записей не происходит, и мы получаем откровенную утечку памяти. Но хуже того, после того как мы удаляем элемент из контейнера (не предпринимая дополнительных действий), мы и не сможем удалить запись, вызвав для неё delete. Это потому, что после вызова erase() мы потеряли к записи единственный путь доступа через итератор (в коде показан цикл с erase(), так наглядней, что эквивалентно clear(), эффект которого, будет тем же самым).

Вывод, который может быть сделан из примера, выглядит так:

  • помещая в контейнеры не объекты, а указатели на них, можно заметно снизить вычислительные затраты на манипуляции с ними (но всегда ли принципиален этот выигрыш при нынешних вычислительных мощностях?).

  • замена объектов на указатели в контейнерах делает код во много раз более опасным в смысле скрытых тонких ошибок, причём такого серьёзного уровня, которые могут дальше приводить к краху приложения.

Здесь некоторую помощь могут оказать умные указатели из последних стандартов C++ (shared_ptr или weak_ptr, но не unique_ptr и не старый добрый и проблемный auto_ptr), нам для этого в предыдущем коде достаточно сменить 4 строчки:

В Windows для shared_ptr необходим #include <memory> , а в других системах не обязательно.

И поведение приложения существенно изменится:

16-2

Но не следует безоглядно обольщаться, так как и умные указатели, снимая одни, порождают другие потенциальные заботы (такие как циклические ссылки и др. о которых достаточно много написано).

Рассылка новых уроков по программированию:

Адаптеры. STL (часть 15)




stack, programming for beginners

Отдельной категорией библиотеки стандартных шаблонов являются адаптеры. Адаптеры — это не новые понятия или реализации, а адаптация уже существующих понятий библиотеки под конкретные, часто использующиеся цели. Часто такая адаптация делается посредством ограничения функциональности базового понятия под запросы адаптера. В библиотеке представлены адаптеры контейнеров, итераторов и функций.

Проще всего показать, как появляются адаптеры на примере адаптеров контейнеров: stack (стек), queue (очередь), priority_queue (очередь с приоритетами). Уже из их перечисления понятно, что:

  • Это очень широко и часто используемые структуры данных;

  • Для них не нужны какие-то отдельные реализации. Для обеспечения их функциональности можно использовать (адаптировать) в качестве базового любой из стандартных контейнеров STL, который обеспечивает операции типа push_back, pop_back или pop_front (в зависимости от типа адаптера);

  • «Лишние» операции из арсенала базового контейнера для адаптера нужно исключить (чтобы не создавать искушения … например, операцию индексации, если vector используется для адаптации стека);

И, вместо громоздких шаблонных синтаксических определений (заголовочные файлы <stack>, <queue> и т. п.), обратимся к примерам…

Начнём со стека: стек характерен тем, что получить доступ к его элементам можно лишь с одного конца, называемого вершиной стека. Это коллекция данных, функционирующая по принципу LIFO (Last In — First Out). Вот такой простенький пример раскрывает нам практически всю функциональность стека:

Проанализируем код и сделаем некоторые выводы:

  • Первое определение stack<string> (мы его дальше не используем) объявляет переменную стек, элементами которого являются строки. Обращаем внимание на то, что объекты string сами по себе являются контейнерами STL. Таким образом, стек может содержать элементы любой глубины вложенности контейнеров (что свойственно и любым другим контейнерам STL).

  • Такое определение ( stack<string> ) вы сможете увидеть, как описание стека в подавляющем большинстве примеров. Многие авторы и не подозревают, что может быть по-другому. Но мы воспользуемся другим определением: stack< string, vector<string> > — это стек строк (во многом эквивалентный предыдущему), но построенный на базовом классе vector<string>. В качестве базовых могут использоваться, например, vector, list и deque, или даже ваш собственный контейнерный класс, расширяющий базовые. По умолчанию (так как в 1-м определении) используется база deque. Иногда спрашивают: почему нельзя написать (определить так в реализации stack): stack< vector<string> > (убрав дублирование string)? Потому что (и это вполне возможно) это будет описание совсем другого типа: стек векторов строк (см. выше замечание о структурной вложенности контейнеров).

  • Дальше следует инициализация начального состояния из вектора строк. Отметьте, что такой трюк допустим только в стандарте C++11.

  • Дальше мы видим практически все операции (методы), требуемые от стека: push() — заталкивание объекта в стек, top() — получение ссылки на элемент в вершине стека, pop() — выбрасывание верхнего элемента, size() — текущий размер стека, empty() — проверка на пустоту.

  • Как легко понять, адаптер stack потерял присущие базовому контейнеру методы (at(), [ ] и др.), но приобрёл (переопределением) свои собственные (push(), pop()).

Теперь посмотрим что из этого получилось:

stack, programming for beginners

Разобравшись со стеком теперь элементарно просто перенести аналогии на очередь. В противоположность стеку — это коллекция данных, функционирующая по принципу FIFO (First In — First Out). (Это такая «труба», в один конец которой что-то втекает, а из другого конца затем вытекает.)

Специально оставим для очереди пример практически неизменным, внеся правки, требуемые семантикой языка:

От нас потребовали изменить:

  • Очередь не может быть построена на базовом контейнере vector у которого нет метода pop_front(), но может строиться на list, deque, или любом другом контейнере, реализующем базовый набор методов (front(), push_back(), pop_front()), по умолчанию используется deque.

  • У адаптера queue нет метода top(), а есть метод аналогичного смысла front().

И в итоге получаем (сравните с результатами для стека!):

stack, programming for beginners

Рассылка новых уроков по программированию:

Обобщённые численные алгоритмы. STL (часть 14)




Обобщённые численные алгоритмы STL c++Следующую, очень нужную и очень мощную группу алгоритмов STL представляют обобщённые численные алгоритмы (заголовочный файл <numeric>). Это не какие-то особые вычислительные методы, как можно подумать исходя из названия. Это алгоритмы, позволяющие применять общеизвестные библиотечные или свои собственные вычислительные функции ко всей совокупности элементов контейнера. А поскольку так, то и вызываются они подобно всем другим алгоритмам STL. Используются такие обобщённые алгоритмы, главным образом в математических вычислениях применительно к контейнерам, содержащим числовые элементы. Но это совсем не обязательно. И если вас не интересуют численные вычисления (например из области цифровой обработки сигналов), то вы можете просто безболезненно пропустить эту часть изложения…

Перечислим представленные STL обобщённые численные алгоритмы: iota (создание монотонно возрастающей последовательности), accumulate (накопление), inner_product (скалярное произведение), partial_sum (частичная сумма), adjacent_difference (смежная разность).

Иллюстрацию работы лучше всего провести на самом используемом и интуитивно понятном алгоритме accumulate. Этот алгоритм редуцирует (уменьшает размерность) контейнера с накоплением значений. В частности, для простых числовых значений он сворачивает vector<> или list<> до одиночного скалярного результирующего значения.

Алгоритм accumulate (как, впрочем, и большинство других) имеет 2 синтаксические формы:

В 1-й форме (она менее интересная) алгоритм суммирует значения элементов контейнера. Не забываем при этом, что для типа string, например, операция ‘+‘ означает конкатенацию, склеивание). Во 2-й форме алгоритм накапливает результат бинарной операции (функции 2-х переменных), применяемой к накапливаемому значению (аккумулятору) и поочерёдно к каждому элементу контейнера.

Непонятно? Это мощная техника, и сейчас всё станет понятно из примера…

В математической статистике находят применение несколько видов среднего значения для числовой последовательности:

  • Среднее арифметическое:

    sa1

  • Среднее геометрическое:

    sg2

  • Среднее гармоническое:

    sr3

  • Среднее квадратическое:

    sq4

Не станем углубляться в смысл каждого из вариантов. Мы сделаем приложение, которое подсчитывает эти средние и ещё некоторые характеристики (дисперсию, средне квадратичное отклонение) для вводимой числовой последовательности. Входную последовательность вводим либо с терминала, либо перенаправлением из предварительно подготовленного файла данных:

Как легко видеть, каждая из записанных выше сложных математических формул вычисляется всего лишь в одну строку, используя технику обобщённых алгоритмов:

Обобщённые численные алгоритмы stl в c++

Здесь мы можем наблюдать известное соотношение, которое подтверждает корректность наших вычислений. Заключается оно в том, что для любой последовательности чисел среднее арифметическое больше или равно среднего геометрического, которое, в свою очередь, больше или равно среднего гармонического. Причём равенство в этих утверждениях достигается только если все члены числовой последовательности равны между собой:

Обобщённые численные алгоритмы stl в c++

Возвратимся к изучению кода. Первый вызов алгоритма accumulate( b, e, 0. ) демонстрирует 1-ю форму использования: значения контейнера суммируются с начальным значением 0.0.

Предупреждение!: Запись точки в константе начального значения, указывающая, что это вещественное значение — принципиально важна. Без этого код будет компилироваться даже без предупреждений, но выполняться с совершенно неверными и крайне сложно толкуемыми результатами! Это связано с тем, что алгоритмы определены как template, и тип 3-го параметра определит для какого типа данных будут задействованы внутренние операции при накоплении.

Все остальные (4 штуки) вызовы accumulate() используют 2-ю форму вызова — передают 4-м параметром функцию накопления. Как видно из примеров, она принимает параметрами текущее накопленное значение и очередной элемент контейнера. А возвращает результат накапливающей операции. Для наглядности, все накапливающие функции записаны в примере в простом и ясном виде. На практике, чтобы избежать зависимости от типа обрабатываемых данных, их также обычно записывают, как шаблонные функции. Тогда это может выглядеть так:

Наконец, обратите внимание, если не обратили до сих пор, что при накоплении сумм мы используем начальное значение 0 (3-й параметр accumulate() ), а при накоплении произведений, естественно, 1, с соответствующим типом данных.

Рассылка новых уроков по программированию:

Сортировка структур. STL Часть 13




сортировка структур с++, stl для начинающихПоказанные в предыдущей части разнообразные сортировки — гибкий и красивый механизм на все случаи жизни. Вот только на практике сортировать в чистом виде последовательности почти никогда не приходится. Это всё из области учебно-показательных задач. На практике куда чаще стоит задача сортировать достаточно объёмные структуры данных (объёмные даже не по своему размеру, а по числу своих полей). И сортировать (или пересортировывать) их предстоит по значениям самых разных полей этих самых структур. Но и здесь на помощь приходят алгоритмы STL, особенно при использовании их совместно с функторами.

Посмотрим типовую модельную задачу, которую мы уже видели раньше — описание учебной группы или факультета:

Программа запрашивает номер поля data, по которому будет вестись сортировка (на самом деле — сравнение). Если этот номер вводится, как положительное число, то сортировка по этому полю идёт в порядке возрастания. Если же номер вводится со знаком минус — то порядок сортировки меняется на обратный:

сортировка структур с++, stl для начинающихсортировка структур с++, stl для начинающих

Обратите внимание, что алгоритму сортировки совершенно безразлично что сортировать: если это числовые данные, то по величине, а если строчные, то в лексографическом порядке.

Рассылка новых уроков по программированию:

Сортировка. STL (часть 12)




сортировка в с++, stlСовершенно особую группу алгоритмов составляют сортировки — сортировать в практике приходится самые разнообразные объекты и по самым разнообразным критериям. Анализ алгоритмов сортировки хорошо и обстоятельно изучен, как ни один другой раздел вычислительной математики. Основным вопросом любого алгоритма сортировки является его вычислительная сложность — число операций сравнения-обмена, требуемых для сортировки последовательности длины N, так называемое O( N ).

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

Эффективными (по сложности) из всего множества методов являются только несколько методов быстрых рекурсивных сортировок. Они то и представлены в реализациях алгоритмов STL. Стандарты не настаивают на жёстком ограничении на внутренних механизмах их реализации, поэтому могут быть варианты в зависимости от используемой библиотеки.

Теперь, обладая некоторыми знаниями об алгоритмах, функторах и общем состоянием дел с сортировками, мы готовы рассмотреть варианты реализации всего этого в своём коде. Для начала мы разнообразными способами сортируем числовые последовательности. Пример великоват, но он предназначен не только, и не сколько, для иллюстрации, как для последующего самостоятельного экспериментирования:

Перед компиляцией программы, в MVS нажмите Alt+F7 и внесите Аргументы команды ?30 +3:сортировка в с++

Некоторая громоздкость примера связана с тем, что мы в одном коде объединяем и все предоставленные STL алгоритмы сортировки, и разнообразные тестовые сортируемые последовательности, например:

  • Несколько случайных (?) последовательностей длины 30, сортируемых с использованием (3) функтора сравнения, и детализированным (+) выводом входной и выходной последовательности:

    сортировка в с++

  • Инверсная (обратная, убывающая) последовательность, сортируемая (6) «в куче» с использованием бинарного дерева (аргументы команды -30 +6 ):сортировка в с++

  • Длинная (1000000) последовательность, в тех же, что и предыдущий случай, условиях, но с выводом только диагностики корректности результата (аргументы команды -1000000 6 ):

    сортировка в с++

Библиотека STL предоставляет 3 группы алгоритмов сортировки:

  • sort() — наиболее быстрая сортировка в среднем ( O( N * log( N ) ), но которая может «проваливаться» в худшем случае до O( N * N ) (а это очень плохой показатель);

  • sort_heap() — сортировка в «в куче», с использованием бинарного дерева, сложность которой всегда не хуже O( N + N * log( N ) ) (это хуже sort() в среднем, но лучше в наихудшем случае);

  • stable_sort() — «устойчивая» сортировка слиянием, устойчивость которой означает, что она сохраняет относительный порядок равных элементов после сортировки. Это иногда очень важно. Сложность этого алгоритма не намного уступает sort();

Используйте тот алгоритм, который наиболее соответствует ограничениям вашей задачи.

Рассылка новых уроков по программированию:

Функциональные объекты. STL (часть 11)




функтор, с++ для начинающих, функциональный объект, библиотека стл, stlВ предыдущих обсуждениях уже неоднократно мелькал такой термин как функтор, но особую актуальность он приобретает применительно к алгоритмам. Теперь пришло время разобраться с этим понятием. Функтор — это сокращение от функциональный объект, представляющий собой конструкцию, позволяющую использовать объект класса как функцию. В C++ для определения функтора достаточно описать класс, в котором переопределена операция ().

То, как из объекта образуется функция, легко показать на таком простом примере:

Уже из такого простого примера видно следующее: операция () в классе может быть переопределена (точнее определена, поскольку она не имеет реализации по умолчанию) с произвольным числом, типом параметров и типом возвращаемого значения (или даже вовсе без возвращаемого значения). В итоге:

функтор, с++ для начинающих, функциональный объект, библиотека стл, stl

Выгода функтора состоит в том, что а). его можно параметризовать при создании объекта (перед вызовом) используя конструктор объекта с параметрами и б). может создаваться временный объект исключительно на время выполнения функционального вызова. Это иллюстрируется примером такого упрощённого целочисленного калькулятора:

Здесь в строке cout << calculate( oper )( op1, op2 ) действия выполняются последовательно:

  • создаётся временный объект класса calculate конструктором с параметром oper;

  • для этого объекта выполняется метод () (функциональный вызов) с двумя параметрами;

  • операция, которая будет выполнена в этом функциональном вызове, зависит от того параметра oper, с которым был сконструирован объект;

  • функциональный вызов возвращает значение результата операции;

  • созданный временный объект, сразу же после этого разрушается (если бы у него был описан деструктор, то он бы вызывался в этой точке);

И в итоге мы получаем:

функтор, с++ для начинающих, функциональный объект, библиотека стл, stl

Но особо широкое применение функторы приобрели в алгоритмах STL, рассмотренных ранее, когда они передаются в вызов в качестве параметра, вместо функции, определяющей действие или предикат алгоритма.

Рассылка новых уроков по программированию:

Алгоритмы. STL (часть 10)




алгоритмы stl, find(), copy(), copy_if(), move(), swap_ranges(), remove_copy(), remove_copy_if(), merge(), set_intersection(), set_difference()Контейнеры STL представляли бы собой красивую выдумку достаточно далёкую от практического использования (как и было в первые годы существования STL), если бы не следующее обстоятельство: из-за единой общей природы всех контейнеров основные алгоритмы, представляющие интерес на практике, могут быть реализованы в обобщённом виде, применимом к любым типам контейнеров. Алгоритмы — это самая объёмная и самая востребованная часть библиотеки. Предоставляется настолько много алгоритмов, что для детального описания их всех не хватит и объёмной книги. Ниже мы совершенно условно поделим их на группы и назовём по именам (и тоже далеко не все), и только по некоторым построим примеры использования.

for_each() Наиболее часто используемый алгоритм — это for_each(): выполнение действия для группы элементов (возможно всех) контейнера. Ниже показано несколько примеров работы алгоритма for_each() для массива и вектора, точно также этот алгоритм может использоваться с любым контейнером STL:

алгоритмы stl, find(), count(), count_if(), search(), binary_search(), min(), max(), minmax_element(), min_element(), max_element(), equal()

Примечание: Строкой 3 показана работа новой (введена стандартом C++11) конструкции for( auto &x : …), которая имеет подобный эффект присвоения и может применяться и к массивам и к контейнерам (в функции-операторе вывода вектора в поток показан такой вариант). Эта конструкция, вообще то говоря, не является составной частью библиотеки STL или алгоритмов, но имеет тот же эффект, что и алгоритм for_each(): применить последовательно ко всем элементам коллекции.

Этот пример показывает основную логику организации всех алгоритмов: к указанному диапазону (не обязательно ко всей коллекции), ограниченному итератором начала и конца (зачастую указываемых первыми 2-мя параметрами) применяется поочерёдно функция, функтор, или предикат (функция, возвращающая логиxческий результат, позволяющий произвести отбор по какому-либо признаку).

find() Следующий по значимости алгоритм — это find(). Как интуитивно понятно из имени, это поиск некоторого элемента в коллекции. Обратите внимание, что многие контейнеры имеют метод find(), который для объекта будет вызываться как obj.find(…), в то время как алгоритм будет вызываться как find( obj:iteator, … ).

Собственно, это даже не один этот алгоритм, а целая обширная их группа, которую можно объединить по признаку того, что они отбирают элементы коллекции по какому-то признаку, условию, предикату: find(), find_if(), find_if_not(), find_first_of(), find_end(), adjacent_find(). В эту же группу, с некоторой натяжкой, можно отнести и count(), count_if(), search(), binary_search(), min(), max(), minmax_element(), min_element(), max_element(), equal() и др.

Ещё одна условная группа — это алгоритмы, некоторым образом «тасующие» коллекцию, переставляющие элементы местами, меняющие значения: fill(), replace_copy(), reverse(), rotate(), rotate_copy(), shuffle(), random_shuffle(), transform(), replace(), replace_if() и др.

Ещё группа — это алгоритмы работающие с 2-мя коллекциями, копирующие и перемещающие содержимое (причём, возможно, между коллекциями разного вида, например, vector<> в set<>): copy(), copy_if(), move(), swap_ranges(), remove_copy(), remove_copy_if(), merge(), set_intersection(), set_difference() и др.

И, наконец, совсем особая группа алгоритмов связана с разнообразными сортировками элементов внутри коллекции: sort(), stable_sort(), is_sorted(), is_sorted_until() и др. Эту интересную группу мы отложим на потом, для отдельного обстоятельного рассмотрения.

При таком обилии реализованных алгоритмов и число которых в библиотеке со временем возрастает, и при том, что большинство из них вообще толком нигде не описаны в литературе, возникает естественный вопрос: как разобраться во всём этом разнообразии? Эти сложности снимаются тем что:

  • Все объекты STL (контейнеры, алгоритмы) описаны в синтаксисе шаблонов (template). Поэтому их описания обязательно должны включаться в компилируемый код в составе своих заголовочных файлов (хедер-файлов).

  • Отправляйтесь в стандартный каталог </usr/include/c++> и найдите там хедер-файлы файлы вида stl_algo* — в них вы найдёте все прототипы функций алгоритмов. Более того, там же каждому прототипу предшествует обстоятельный комментарий, объясняющий назначение алгоритма, и объясняющий параметры вызова.

  • Рассмотрите примеры кода, использующие несколько основных алгоритмов STL — в сети их множество. По аналогии элементарно просто воспроизвести поведение и всех остальных алгоритмов.

Примечание: Вот из-за того, что библиотеки шаблонных классов определены в терминах template, сообщения об синтаксических ошибках компиляции становятся а). многословными, на десятки строк сообщений и б). ужасно невнятными для поиска ошибок. Это оборотная сторона медали такого мощного механизма как template, и к этому нужно быть готовым.

Как и было сказано выше, изучение примеров снимет множество вопросов, а поэтому приступим к коду … Теперь внимательно следите за руками (алгоритмов STL такое множество, что комментировать каждый — за пределами разумного объёма изложения, но все они работают подобно один другому):

Здесь использованы контейнеры для char (как компактные, но самые неприятные в работе), над которыми выполняются разнообразные алгоритмы практически всех обозначенных групп:

алгоритмы stl, find(), count(), count_if(), search(), binary_search(), min(), max(), minmax_element(), min_element(), max_element(), equal()

Рассылка новых уроков по программированию:

Контейнеры STL: set и multiset. Часть 9




класс set и multiset c++, контейнеры STL, set multiset для начинающихДостаточно часто на практике требуется контролировать только принадлежность тех или иных объектов к некоторому подмножеству. Такие коллекции и в классической математике называются множество, к которому конкретный объект может или принадлежать, или нет. И библиотека STL предоставляет такой вид контейнеров, который так и называется — set<> (множество).

Каждый элемент контейнера set<> содержит только значение ключа (не пару, как для map<>), поэтому такой тип контейнера очень эффективно реализует операцию проверки наличия значения в наборе. Этот контейнер предоставляет много общих методов в сравнении с map<>, хотя они здесь имеют некоторые вырожденные качества (и порой бесполезные):

  • Операция вставки (метод insert()) нового значения ключа к множеству возвращает пару типа <итератор, bool> (точно, как для map<>). В этой паре второй компонент (second, типа bool) указывает на успешность операции: если это true, то первый компонент возвращаемого результата (first) даёт итератор нового добавленного элемента. Но это достаточно бессмысленное возвращаемое значение: если возвращается true, то новое значение успешно добавлено к множеству, если же возвращается false, то оно уже присутствует в множестве ранее — в обоих случаях конечное состояние множества будет идентичным (поэтому на практике возвращаемое значение insert() обычно даже не проверяют … а многие и не знают, что там вообще предусмотрено возвращаемое значение вообще);

  • Для этого контейнера реализован метод count() (как для multimap<>), но, поскольку значение ключа может присутствовать в множеств только в единичном экземпляре, метод count() может возвратить только 0, если такое значение отсутствует, и 1 когда такое значение присутствует.

  • Для контейнера реализован метод found(), который возвращает итератор элемента, если значение найдено, и итератор со значением end() если значение отсутствует в множестве.

Для демонстрации сказанного создадим приложение, которое N раз (параметр, задаваемый в команде запуска приложения) в цикле генерирует псевдослучайное число в фиксированном диапазоне [0…lim] и помещает его в множество. Понятно, что при возрастании N больше чем lim (а тем более при N намного больше lim), каждое число диапазона [0…lim] будет генерироваться всё большее и большее число раз:

класс set и multiset c++, контейнеры STL, set multiset для начинающих
Аргумент команды в свойствах отладки не задан

класс set и multiset c++, контейнеры STL, set multiset для начинающих
Аргумент команды в свойствах отладки равен 5
класс set и multiset c++, контейнеры STL, set multiset для начинающих
Аргумент команды в свойствах отладки равен 100

Другим близким типом контейнера является multiset<>, позволяющее каждому значению ключа быть не уникальным, и находиться в множестве сколько угодно раз. Эти два контейнера (set<> и multiset<>) настолько подобные, что в показанном выше приложении мы заменим всего 2 строчки (метод count() для multiset<> возвращает число вхождений значения в множество):

Но поведение приложения радикально меняется (для сравнения рядом показаны результаты 2-х приложений в идентичных условиях):

класс set и multiset c++, контейнеры STL, set multiset для начинающих

Видим, что для мультимножества значения 1, 2, 13, 17… отсутствуют в контейнере, а значение 18, например, присутствует в нём 5 раз.

Рассылка новых уроков по программированию: