- •Цель работы
- •2 Техническое обеспечение
- •3 Программное обеспечение
- •4 Постановка задачи
- •Общие сведения
- •5.1 Шаблоны классов
- •5.1.1 Пример класса-шаблона
- •5.1.2 Параметры шаблона, задаваемые по умолчанию
- •5.1.3 Специализация шаблона класса
- •5.1.4 Статические элементы в шаблонах
- •5.1.5 Шаблоны классов с шаблонами
- •5. 2 Шаблоны функций
- •5.3 Обобщенные алгоритмы и функторы
- •Варианты заданий
- •7 Указания к выполнению лабораторной работы
- •9 Контрольные вопросы
5.1.1 Пример класса-шаблона
Листинг 11.1. Простой массив – шаблон
#include <stdexcept>
template <class T, std::size_t N>
// параметры шаблона
class Array
{
public:
// типы
typedef T value_type;
typedef T &reference;
typedef const T &const_reference;
typedef std::size_t size_type;
static const size_type static_size = N;
// размер массива
Array(const T &t = T());
// конструктор
size_type size() const
// получение размера
{ return static_size; }
reference operator[](const size_type &i)
// доступ к элементам
{ rangecheck(i); return elem[i]; }
const_reference operator[](const size_type &i) const
{ rangecheck(i); return elem[i]; };
private:
void rangecheck (const size_type &i) const // проверка индекса
{
if (i >= size()) { throw std::range_error("Array - range!"); }
}
T elem[N]; // поле-массив
};
template <typename Т, std::size_t N>
// реализация конструктора
Array <T,N>::Array(const T &t)
{ for (int i = 0; i<N; i++) elem[i] = t; }
Функциональность класса – минимальная. В начале класса-шаблона определено несколько типов-синонимов. Проверка индекса в операциях индексирования [] выполняется приватной функцией rangecheck(), которая генерирует стандартное исключение при «вылете» за границу массива.
Конструктор инициализации является и конструктором по умолчанию. Конструктор класса-шаблона задан внешним образом: определение метода должно начинаться со слова template вместе с параметрами шаблона:
template <typename Т, std::size_t N>
Тип класса-шаблона Array <T, N>:: задается в качестве префикса. В теле метода для всех имен класса префикс может отсутствовать. Для методов, определенных внутри шаблона, префиксы не указываются.
Параметру присвоено значение по умолчанию
const Т &t = Т()
При инстанцировании шаблона (подстановка конкретного типа) эта конструкция означает инициализацию нулем для элементарных встроенных типов; а для классов влечет вызов конструктора по умолчанию (то есть класс, используемый в качестве подставляемого аргумента шаблона, должен иметь конструктор по умолчанию).
Данную конструкцию можно использовать не только в списке параметров, но в любом месте шаблона, где допускается объявить переменную и присвоить ей значение. Например, в цикле инициализации массива можно было бы написать:
elem[i] = Т();
что превращается в корректную конструкцию при подстановке любого типа, как встроенного, так и реализованного.
В классе-шаблоне разрешено использовать инициализацию нулем и в списке инициализации конструктора. Если отказаться от присвоения значения по умолчанию и использовать явное обнуление, конструктор класса-шаблона примет вид:
template <typename Т, std::size_t N>
// реализация конструктора
Array <T, N>::Array():elem() { }
Массив будет проинициализирован нулем, если вместо Т подставить встроенный тип. Для классов тогда будет вызываться конструктор без аргументов.
Использовать такой шаблон просто.
Array <int, 10> w;// инициализируем нулем
Array <int, 10> t(0);// инициализируем нулем
Array <int, 10> r(t);// конструктор копирования
Array <double, 50> р(2);// все элементы = 2
for (int i = 0; i < p.size(); i++)
cout << p[i] << ' ';// доступ справа
for (int i = 0; i < p.size(); i++)
p[i] = i;// доступ слева
В качестве типа-аргумента могут подставляться любые возможные типы, определение которых видимо в точке объявления переменной-массива.
Array <void*, 100> рр; // массив указателей
Array<Pair <int, int>, 100> mр; // массив объектов-пар
Array<Pair<long,int>*,10> *mpp; // указатель на массив
// указателей на пары
Array<date, 10> dd; // массив дат
Класс date должен иметь конструктор без аргументов, вызываемый в конструкторе массива.
Действительно также и присваивание:
w = г; // присваиваем - типы одинаковы
Копирование и присваивание работают только для массивов с одинаковым типом элементов и размером массива. Изменяя тип элементов и (или) размер, получаем массивы разных типов. При попытках использовать оператор присваивания или конструктор копирования с такими разнотипными массивами компилятор выдает сообщение о невозможности преобразования типов. Например, нельзя написать присвоение
р = t;
Слева массив с типом элементов double, а справа int. Преобразование типов по умолчанию не выполняется.
Объекты такого класса-шаблона можно передавать как параметры и получать как результат из функций. Необходимо помнить, что при передаче (и при возврате) массива по значению вызывается конструктор копирования, поэтому лучше такой массив передавать по ссылке.
Array<double, 10> F(const Array <int, 10> &t);
Эта функция получает ссылку на массив из 10 элементов типа int, а возвращает массив из 10 элементов типа double. Нужно также помнить, что преобразование типов по умолчанию не работает.
