Шаблонная функция с вектором

Урок №173. Шаблоны функций

Обновл. 4 Сен 2021 |

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

Видео:vector | Библиотека стандартных шаблонов (stl) | Уроки | C++ | #1Скачать

vector | Библиотека стандартных шаблонов (stl) | Уроки | C++ | #1

Шаблоны функций

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

Всё отлично до тех пор, пока мы работаем с целочисленными значениями. А что, если нам придется работать и со значениями типа double? Вы, вероятно, решите перегрузить функцию max() для работы с типом double:

Теперь у нас есть две версии одной функции, которые работают с типами char, int, double и, если мы перегрузим оператор > , даже с классами! Однако, поскольку C++ требует, чтобы мы указывали типы наших переменных, нам приходится записывать несколько версий одной и той же функции, где единственное, что меняется — это тип параметров.

А это, в свою очередь, головная боль для программистов, так как поддерживать такой код непросто как по затраченным усилиям, так и по времени. И самое важное то, что это нарушает одну из концепций эффективного программирования — сократить до минимума дублирование кода. Правда, было бы неплохо написать одну версию функции max(), которая работала бы с параметрами ЛЮБОГО типа?

Это возможно. Добро пожаловать в мир шаблонов!

Если посмотреть определение слова «шаблон» в словаре, то увидим следующее: «Шаблон — это образец, по которому изготавливаются похожие изделия». Например, шаблоном является трафарет — объект (например, пластинка), в котором прорезан рисунок/узор/символ. Если приложить трафарет к другому объекту и распылить краску, то получим этот же рисунок, прилагая минимум усилий, быстро и, что не менее важно, мы сможем сделать десятки этих рисунков разных цветов! При этом нам нужен лишь один трафарет и нам не нужно определять цвет рисунка заранее (до использования трафарета).

В языке C++ шаблоны функцийэто функции, которые служат образцом для создания других подобных функций. Главная идея — создание функций без указания точного типа(ов) некоторых или всех переменных. Для этого мы определяем функцию, указывая тип параметра шаблона, который используется вместо любого типа данных. После того, как мы создали функцию с типом параметра шаблона, мы фактически создали «трафарет функции».

При вызове шаблона функции, компилятор использует «трафарет» в качестве образца функции, заменяя тип параметра шаблона на фактический тип переменных, передаваемых в функцию! Таким образом, мы можем создать 50 «оттенков» функции, используя всего лишь один шаблон!

Видео:Шаблоны функций. Шаблонные функции c++. template typename. template class. Урок #41Скачать

Шаблоны функций. Шаблонные функции c++. template typename. template class. Урок #41

Создание шаблонов функций

Сейчас вам, вероятно, интересно, как создаются шаблоны функций в языке C++. Оказывается, это не так уж и сложно. Рассмотрим еще раз целочисленную версию функции max():

Видео:Шаблоны классов с++ примеры. Обобщенные классы. Изучение С++ для начинающих. Урок #126Скачать

Шаблоны классов с++ примеры. Обобщенные классы. Изучение С++ для начинающих. Урок #126

Шаблоны и шаблонные функции в C++. Введение

Видео:[C++] Шаблонные функции и шаблонные классыСкачать

[C++] Шаблонные функции и шаблонные классы

Шаблонные функции

Давайте рассмотрим простой пример. Допустим, у нас есть функция, которая меняет местами значения двух переменных типа int:

Теперь, допустим, у нас в функции main так же есть две переменные типа double, значения которых тоже нужно обменять. Функция для обмена значений двух переменных типа int нам не подойдет. Напишем функцию для double:

И теперь перепишем main:

Как видите, у нас алгоритм абсолютно одинаковый, отличаются лишь типы параметров и тип переменной temp. А теперь представьте, что нам еще нужны функции для short, long double, char, string и еще множества других типов. Конечно, можно просто скопировать первую функцию, и исправить типы на нужные, тогда получим новую функцию с необходимыми типами. А если функция будет не такая простая? А вдруг потом еще обнаружится, что в первой функции была ошибка? Избежать всего этого можно, например, «шаманством» с препроцессором, но это нам ни к чему, нам помогут шаблоны.

Для начала, заглянем в википедию и посмотрим, что же такое шаблоны:

Шабло́ны (англ. template) — средство языка C++, предназначенное для кодирования обобщённых алгоритмов, без привязки к некоторым параметрам (например, типам данных, размерам буферов, значениям по умолчанию).
https://ru.wikipedia.org/wiki/Шаблоны_C++

Итак, описание шаблона начинается с ключевого слова template за которым в угловых скобках (« ») следует список параметров шаблона. Далее, собственно идет объявление шаблонной сущности (например функция или класс), т. е. имеет вид:

Теперь давайте напишем шаблонную функцию my_swap. Исходя из упомянутой выше структуры объявления шаблона следует, что наша функция будет выглядеть так:

typename в угловых скобках означает, что параметром шаблона будет тип данных. T — имя параметра шаблона. Вместо typename здесь можно использовать слово class: template В данном контексте ключевые слова typename и class эквивалентны (лично мне больше нравится typename, а кому-то class). Далее, в тексте шаблона везде, где мы используем тип T, вместо T будет проставляться необходимый нам тип.

теперь давайте напишем функцию main:

Как видите, после имени функции в угловых скобках мы указываем тип, который нам необходим, он то и будет типом T. Шаблон — это лишь макет, по которому компилятор самостоятельно будет генерировать код. При виде такой конструкции: my_swap компилятор сам создаст функцию my_swap с необходимым типом. Это называется инстанцирование шаблона. То есть при виде my_swap компилятор создаст функцию my_swap в которой T поменяет на int, а при виде my_swap будет создана функция с типом double. Если где-то дальше компилятор опять встретит my_swap , то он ничего генерировать не будет, т.к. код данной функции уже есть(шаблон с данным параметром уже инстанцирован).

Таким образом, если мы инстанцируем этот шаблон три раза с разными типами, то компилятор создаст три разные функции

Видео:[C++] STL: VectorСкачать

[C++] STL: Vector

Вывод типа шаблона исходя из параметров функции

На самом деле, мы можем вызвать функцию my_swap не указывая тип в угловых скобках. В ряде случаев компилятор может это сделать за вас.

рассмотрим вызов функции без указания типа:

Наша шаблонная функция принимает параметры типа T&, основываясь на шаблоне, компилятор видит, что Вы передаете в функцию аргументы типа int, поэтому может самостоятельно определить, что в данном месте имеется ввиду функция my_swap с типом int. Это deducing template arguments. Теперь давайте напишем пример посложнее. Например, программу сортировки массива(будем использовать сортировку «пузырьком»). Естественно, что алгоритм сортировки один и тот же, а вот типы элементов в массиве будут отличаться. Для обменивания значений будем использовать нашу шаблонную функцию my_swap. Приступим:

Source arrays: 10 5 7 3 4 7.62 5.56 38 56 9 Sorted arrays: 3 4 5 7 10 5.56 7.62 9 38 56

Как видите, компилятор сам генерирует out_array для необходимого типа. Так же он сам генерирует функцию bubbleSort. А в bubbleSort у нас применяется шаблонная функция my_swap, компилятор сгенерирует и её код автоматически. Удобно, не правда ли?

Видео:#7. Реализация динамического массива на С++ с помощью std::vector | Структуры данныхСкачать

#7. Реализация динамического массива на С++ с помощью std::vector | Структуры данных

Введение в шаблонные классы

Шаблонными могут быть не только функции. Рассмотрим шаблонные классы. Начнем с простого примера. Мы добавим в наш предыдущий код функцию, которая будет искать максимум и минимум в массиве. При создании функции «упираемся» в проблему — как вернуть два указателя? Можно передать их в функцию в качестве параметров, а можно вернуть объект, который будет содержать в себе два указателя. Первый вариант при большом кол-ве возвращаемых значений приведет к заваливанию функции параметрами, поэтому я предлагаю сделать структуру:

А какого же типа будут указатели? Можно сделать их void*, но тогда придется постоянно кастовать их к нужному типу, и код станет похож на «Доширак». А что, если сделать эту структуру шаблонной? Попробуем:

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

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

Теперь давайте напишем код шаблонной функции для поиска максимума и минимума:

Теперь мы можем вызывать данную функцию:

Для классов мы должны явно указывать параметры шаблона. В стандарте C++11, устаревшее ключевое слово auto поменяло свое значение и теперь служит для автоматического вывода типа в зависимости от типа инициализатора, поэтому мы можем написать так:

Предлагаю написать еще одну функцию, которая будет выводить объект my_pointer_pair в стандартный поток вывода:

Теперь соберем всё воедино:

Arrays: 10 5 7 3 4 7.62 5.56 38 56 9 min = 3 max = 10 min = 5.56 max = 56

Видео:Базовый курс С++ Часть #81. Вектор std::vectorСкачать

Базовый курс С++ Часть #81. Вектор std::vector

Шаблоны и STL

В комплекте с компилятором Вам предоставляется стандартная библиотека шаблонов (Standart Template Library). Она содержит множество шаблонных функций и классов. Например, класс двусвязного списка(list), класс «пара» (pair), функция обмена двух переменных(swap), функции сортировок, динамически расширяемый массив(vector) и т.д. Всё это — шаблоны и Вы можете их использовать. Для небольшого примера возьмем std::vector:

Заметьте, когда писали std::vector, авторы понятия не имели, элементы какого типа Вы будете хранить.

Шаблоны это слишком большой и мощный инструмент и описать всё в одной статье не представляется возможным. Это было лишь небольшое введение в мир шаблонов. Углубляясь в шаблоны, Вы поразитесь тому, какой мощный это инструмент и какие возможности он предоставляет.

Видео:Лекция 11. Введение в шаблоны II (Программирование на C++, часть 1)Скачать

Лекция 11. Введение в шаблоны II (Программирование на C++, часть 1)

Векторы в C++: для начинающих

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

Быстрый переход по статье:

Видео:Лекция 10. Введение в шаблоны I (Программирование на C++, часть 1)Скачать

Лекция 10. Введение в шаблоны I (Программирование на C++, часть 1)

Шаблонная функция с векторомЧто такое вектор (vector)

Вектор — это структура данных, которая уже является моделью динамического массива.

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

Видео:С++: Шаблонные функцииСкачать

С++: Шаблонные функции

Как создать вектор (vector) в C++

Сначала для создания вектора нам понадобится подключить библиотеку — , в ней хранится шаблон вектора.

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

Далее, чтобы объявить вектор, нужно пользоваться конструкцией ниже:

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

В примере выше мы создали вектор строк.

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

После имени вектора ставим знак равенства и скобки, в которых через пробел указываем значение элементов.

Такой способ инициализации можно использовать только в C++!

Так, чтобы заполнить вектор строками, нам нужно использовать кавычки — «строка» .

Второй способ обратиться к ячейке

Мы знаем, что в векторе для обращения к ячейке используются индексы. Обычно мы их используем совместно с квадратными скобками [] .

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

Вот как она работает на практике:

Давайте запустим эту программу:

Как указать количество ячеек для вектора

Указывать размер вектора можно по-разному. Можно это сделать еще при его инициализации, а можно хоть в самом конце программы. Вот, например, способ указать длину вектора на старте:

Так в круглых скобках () после имени вектора указываем первоначальную длину. А вот второй способ:

Первая строчка нам уже знакома. А вот во второй присутствует незнакомое слово — reserve , это функция, с помощью которой мы говорим компилятору, какое количество ячеек нам нужно использовать.

Вы можете задать логичный вопрос:»А в чем разница?». Давайте создадим два вектора и по-разному укажем их количество ячеек.

Как видим, в первом случае мы вывели три нуля, а во втором: 17, 0, 0.

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

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

Поэтому, когда мы вывели второй вектор, в нем уже находились какие-то рандомные числа — 17, 0, 0. Обычно они намного больше. Можете кстати попробовать создать переменную и вывести ее значение.

Нужно помнить! При использовании второго способа есть некоторый плюс — по времени. Так как для первого способа компилятор тратит время, чтобы заполнить все ячейки нулями.

Видео:14. Специализации шаблонов, числовые и шаблонные параметры шаблоновСкачать

14. Специализации шаблонов, числовые и шаблонные параметры шаблонов

Шаблонная функция с вектором Как сравнить два вектора

Если в середине программы нам понадобиться сравнить два массива, мы, конечно, используем цикл for и поочередно проверим все элементы.

Вектор снова на шаг впереди! Чтобы нам сравнить два вектора, потребуется применить всего лишь оператор ветвления if.

🎬 Видео

Как построить проекцию вектора?Скачать

Как построить проекцию вектора?

13. Шаблоны. Перегрузка и специализация шаблоновСкачать

13. Шаблоны. Перегрузка и специализация шаблонов

Линейная алгебра. Векторы и операции над векторами.Скачать

Линейная алгебра. Векторы и операции над векторами.

➡️ КАК ВЫЧИТАТЬ ВЕКТОРЫ?Скачать

➡️ КАК ВЫЧИТАТЬ ВЕКТОРЫ?

Единичный векторСкачать

Единичный вектор

Шаблоны, часть 2Скачать

Шаблоны, часть 2

Урок 9. Проекции вектора на координатные осиСкачать

Урок 9. Проекции вектора на координатные оси

Орт вектора. Нормировать вектор. Найти единичный векторСкачать

Орт вектора.  Нормировать вектор.  Найти единичный вектор
Поделиться или сохранить к себе: