Читаем Программирование полностью

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

  Данную форму обобщенного программирования, основанную на явных шаблонных параметрах, часто называют параметрическим полиморфизмом (parametric polymorphism). В противоположность ей полиморфизм, возникающий благодаря иерархии классов и виртуальным функциям, называют специальным полиморфизмом (ad hoc polymorphism), а соответствующий стиль — ориентированным программированием (см. разделы 14.3-14.4). Причина, по которой оба стиля программирования называют полиморфизмом (polymorphism), заключается в том, что каждый из них дает программисту возможность создавать много версий одного и того же понятия с помощью единого интерфейса. Полиморфизм по-гречески означает “много форм”. Таким образом, вы можете манипулировать разными типами с помощью общего интерфейса. В примерах, посвященных классу Shape, рассмотренных в главах 16–19, мы буквально работали с разными формами (классами Text, Circle и Polygon) с помощью интерфейса, определенного классом Shape. Используя класс vector, мы фактически работаем со многими векторами (например, vector, vector и vector) с помощью интерфейса, определенного шаблонным классом vector.

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

v.push_back(x); // записать x в вектор v

s.draw(); // нарисовать фигуру s

Для вызова v.push_back(x) компилятор определит тип элементов в объекте v и применит соответствующую функцию push_back(), а для вызова s.draw() он неявно вызовет некую функцию draw() (с помощью таблицы виртуальных функций, связанной с объектом s; см. раздел 14.3.1). Это дает объектно-ориентированному программированию свободу, которой лишено обобщенное программирование, но в то же время это делает обычное обобщенное программирование более систематическим, понятным и эффективным (благодаря прилагательным “специальный” и “параметрический”).

  Подведем итоги.

Обобщенное программирование поддерживается шаблонами, основываясь на решениях, принятых на этапе компиляции

Объектно-ориентированное программирование поддерживается иерархиями классов и виртуальными функциями, основываясь на решениях, принятых на этапе выполнения программы.

Сочетание этих стилей программирования вполне возможно и полезно. Рассмотрим пример.

void draw_all(vector& v)

{

  for (int i=0; idraw();

}

Здесь мы вызываем виртуальную функцию (draw()) из базового класса (Shape) с помощью другой виртуальной функции — это определенно объектно-ориентированное программирование. Однако указатели Shape* хранятся в объекте класса vector, который является параметризованным типом, значит, мы одновременно применяем (простое) обобщенное программирование.

  Но довольно философии. Для чего же на самом деле используются шаблоны?

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

• Используйте шаблоны, когда производительность программы играет важную роль (например, при интенсивных вычислениях в реальном времени; подробнее об этом речь пойдет в главах 24 и 25).

• Используйте шаблоны, когда гибкость сочетания информации, поступающей от разных типов, играет важную роль (например, при работе со стандартной библиотекой языка C++; эта тема будет обсуждаться в главах 20 и 21).

  Шаблоны имеют много полезных свойств, таких как высокая гибкость и почти оптимальная производительность, но, к сожалению, они не идеальны. Как всегда, преимуществам сопутствуют недостатки. Основным недостатком шаблонов является то, что гибкость и высокая производительность достигаются за счет плохого разделения между “внутренностью” шаблона (его определением) и его интерфейсом (объявлением). Это проявляется в плохой диагностике ошибок, особенно плохими являются сообщения об ошибках. Иногда эти сообщения об ошибках в процессе компиляции выдаются намного позже, чем следовало бы.

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже