Сравнение раннего связывания с поздним



 

При обсуждении объектно‑ориентированных языков обычно используются два термина: раннее связывание (early binding) и позднее связывание (late binding). В C++ эти термины связывают с событиями, которые происходят во время компиляции и в период выполнения программы соответственно.

При раннем связывании вызов функции подготавливается во время компиляции, а при позднем – во время выполнения программы.

Раннее связывание означает, что вся информация, необходимая для вызова функции, известна при компиляции программы. Примерами раннего связывания могут служить вызовы стандартных функций и вызовы перегруженных функций (обычных и операторных). Из принципиальных достоинств раннего связывания можно назвать эффективность: оно работает быстрее позднего и часто требует меньших затрат памяти. Его основной недостаток – отсутствие гибкости.

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

Чему отдать предпочтение – раннему или позднему связыванию, зависит от назначения вашей программы. (В действительности в большинстве крупных программ используются оба вида связывания.) Позднее связывание (его еще называют динамическим ) – это одно из самых мощных средств C++. Однако за это могущество приходится расплачиваться потерями в скорости выполнения программ. Поэтому позднее связывание лучше всего использовать только в случае, когда оно существенно улучшает структуру и управляемость программой. Как и все сильные средства, позднее связывание, конечно, стоит использовать, но не злоупотребляя им. Вызванные им потери в производительности весьма незначительны, поэтому, когда ситуация требует позднего связывания, смело берите его на вооружение.

 

Полиморфизм и пуризм

 

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

Некоторые пуристы (в данном случае – борцы за чистоту терминологии объектно‑ориентированного программирования) настаивают, чтобы этот термин использовался только для событий, которые происходят во время выполнения программ. Они утверждают, что полиморфизм поддерживается только виртуальными функциями. Частично эта точка зрения основывается на том факте, что самыми первыми полиморфическими языками программирования были интерпретаторы (для них характерно то, что все события относятся ко времени выполнения программы). Появление транслируемых полиморфических языков программирования расширило концепцию полиморфизма. Однако все еще не утихают заявления о том, что термин полиморфизм должен применяться исключительно к событиям периода выполнения. Большинство С++‑программистов не согласны с этой точкой зрения и считают, что этот термин применим к обоим видам средств. Поэтому вы не должны удивляться, если кто‑то в один прекрасный день станет спорить с вами на предмет использования этого термина!

 

Глава 16: Шаблоны

 

Шаблон – это одно из самых сложных и мощных средств в C++. Он не вошел в исходную спецификацию C++, и лишь несколько лет назад стал неотъемлемой частью программирования на C++. Шаблоны позволяют достичь одну из самых трудных целей в программировании – создать многократно используемый код.

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

 

Обобщенные функции

 

Обобщенная функцияэто функция, перегружающая сама себя.

Обобщенная функция определяет общий набор операций, которые предназначены для применения к данным различных типов. Тип данных, обрабатываемых функцией, передается ей как параметр. Используя обобщенную функцию, к широкому диапазону данных можно применить единую общую процедуру. Возможно, вам известно, что многие алгоритмы имеют одинаковую логику для разных типов данных. Например, один и тот же алгоритм сортировки Quicksort применяется и к массиву целых чисел, и к массиву значений с плавающей точкой. Различие здесь состоит только в типе сортируемых данных. Создавая обобщенную функцию, можно определить природу алгоритма независимо от типа данных. После этого компилятор автоматически сгенерирует корректный код для типа данных, который в действительности используется при выполнении этой функции. По сути, создавая обобщенную функцию, вы создаете функцию, которая автоматически перегружает себя саму.

Обобщенная функция создается с помощью ключевого слова template . Обычное значение слова "template" точно отражает цель его применения в C++. Это ключевое слово используется для создания шаблона (или оболочки), который описывает действия, выполняемые функцией. Компилятору же остается "дополнить недостающие детали" в соответствии с заданным значением параметра. Общий формат определения шаблонной функции имеет следующий вид.

 

 

Определение обобщенной функции начинается с ключевого слова template.

Здесь элемент Ttype представляет собой "заполнитель" для типа данных, обрабатываемых функцией. Это имя может быть использовано в теле функции. Но оно означает всего лишь заполнитель, вместо которого компилятор автоматически подставит реальный тип данных при создании конкретной версии функции. И хотя для задания обобщенного типа в template ‑объявлении по традиции применяется ключевое слово class , можно также использовать ключевое слово typename .

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

 

 

Вот как выглядят результаты выполнения этой программы.

 

 

Итак, рассмотрим внимательно код программы. Строка

 

 

сообщает компилятору, во‑первых, что создается шаблон, и, во‑вторых, что здесь начинается обобщенное определение. Обозначение X представляет собой обобщенный тип, который используется в качестве "заполнителя" . За template ‑заголовком следует объявление функции swapargs() , в котором символ X означает тип данных для значений, которые будут меняться местами. В функции main() демонстрируется вызов функции swapargs() с использованием трех различных типов данных: int , float и char . Поскольку функция swapargs() является обобщенной, компилятор автоматически создает три версии функции swapargs() : одну для обмена целых чисел, вторую для обмена значений с плавающей точкой и третью для обмена символов.

Здесь необходимо уточнить некоторые важные термины, связанные с шаблонами. Во‑первых, обобщенная функция (т.е. функция, объявление которой предваряется template ‑инструкцией) также называется шаблонной функцией . Оба термина используются в этой книге взаимозаменяемо. Когда компилятор создает конкретную версию этой функции, то говорят, что создается ее специализация (или конкретизация ). Специализация также называется порожденной функцией (generated function). Действие порождения функции определяют как ее реализацию (instantiating). Другими словами, порождаемая функция является конкретным экземпляром шаблонной функции.

Поскольку C++ не распознает символ конца строки в качестве признака конца инструкции, template ‑часть определения обобщенной функции может не находиться в одной строке с именем этой функции. В следующем примере показан еще один (довольно распространенный) способ форматирования функции swapargs() .

 

 

При использовании этого формата важно понимать, что между template ‑инструкцией и началом определения обобщенной функции никакие другие инструкции находиться не могут. Например, следующий фрагмент кода не скомпилируется.

 

 

Как отмечено в комментарии, template ‑спецификация должна стоять непосредственно перед определением функции. Между ними не может находиться ни инструкция объявления переменной, ни какая‑либо другая инструкция.

 


Дата добавления: 2018-09-22; просмотров: 466; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!