Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Козин С. В. ООП. Лекции.doc
Скачиваний:
57
Добавлен:
06.07.2020
Размер:
489.47 Кб
Скачать

Шаблоны классов и отношение включения

Шаблон класса может использоваться в качестве компонента другого шаблона. В реализации шаблона Stack в качестве его компонента используется шаблон массива.

// Компонент (часть) template<typename T> class Array { // ... }; // Композит (общий) template <typename T> class Stack { public: // ... private: Array<T> ar; // ... };

Рекурсивное использование шаблонов классов

Шаблоны классов можно применять рекурсивно. Пусть имеется следующий шаблон массива.

template <typename T, int size> class Array { // ... };

Располагая таким определением шаблона, его можно использовать следующим образом:

Array<Array<int, 5>, 10> matrix;

Это определение создает массив, состоящий из 10 элементов, каждый из которых в свою очередь является массивом из 5 элементов типа int. Таким образом, получаем двумерный массив, который может хранить до 10 строк и 5 столбцов.

Если в шаблоне Array перегружен оператор operator[], то к элементам matrix можно обращаться следующим образом matrix[i][j].

Друзья и шаблоны классов

При использовании «друзей» в шаблонах классов возникают определенные трудности. Ограничимся рассмотрением этого вопроса применительно к «дружественным» функциям. Более того, будем рассматривать конкретный пример, в котором для шаблона Array необходимо перегрузить оператор поместить в поток «<<».

Рассмотрим два варианта такой перегрузки.

  1. Легко реализуемый вариант, когда определение перегружающей функции располагается в теле шаблона: недостаток – несоответствие стилю хорошего программирования.

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

Вариант 1.

template <typename T> class Array { public: friend ostream7 operator<<(ostream& os, const Array<T>& rhs) { // ... } };

Вариант 2.

Здесь необходимы следующие дополнительные компоненты:

  • Опережающее объявление для перегружающей шаблонной функции.

  • Указание в теле шаблонного класса на то, что дружественная функция является шаблоном.

// Опережающее объявление для шаблонного класса Array, // необходимо для последующего опережающего объявления // дружественной функции template <typename T> class Array; // Опережающее объявление дружественной функции template<typename T> ostream& operator<< (ostream& os, const Array<T>& rhs); template <typename T> class Array { public: friend ostream& operator<< <>(ostream& os, const Array<T> rhs); // ... }; template <typename T> ostream& operator<<(ostream& os, const Array<T>& rhs) { // ... }

Явная и частичная специализация шаблона класса

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

Частичная специализация – это альтернативное определение шаблона класса, в котором некоторые из параметров шаблона заменены аргументами. Явная специализация имеет место, когда в частичной специализации выполнена замена всех параметров шаблона.

Здесь могут быть полезными следующие понятия:

  • Первичный шаблон.

  • Вторичный шаблон.

Первичный шаблон – это исходный шаблон, а вторичный шаблон – его специализация.

Пример.

// Первичный шаблон template <typename T1, typename T2> class Pair { // ... }; // Частичная специализация. // Зафиксирован второй параметр шаблона template <typename T1> class Pair<T1, int> { // ... }; // Явная специализация. // Зафиксированы все параметры шаблона template <> class Pair<int, int> {

// ... };

Обсуждение.

Основной принцип использования состоит в том, что компилятор выбирает наиболее специализированный шаблон.

Пусть имеется следующий клиентский программный код

Pair <double, double> pr1;

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

Пусть имеется другой клиентский программный код

Pair <double, int> pr2;

В этом случае будет использоваться частичная специализация.

Наконец для клиентского кода

  1. Pair <int, int> pr3;

будет использована явная специализация.