-
Notifications
You must be signed in to change notification settings - Fork 6
Шаблоны классов
Проверка шаблона на корректность происходит только при его использовании. В зависимости от передаваемых параметров шаблон может работать корректно или некорректно. Однако шаблоны достаточно полезны – если есть несколько функций, совершающих одни и те же действия над разными объектами, можно написать шаблон функции. Шаблон задает функцию для работы с разными типами данных. При создании есть параметр типа.
//Пример – определение размера файла.
template <typename/*или class*/ T> unsigned length(FILE *fv)
{
return filelength(fileno(fv)) / sizeof(T); //возвращает размер в байтах, делённый на количество байт в типе
}
FILE *f;
...
unsigned Count = length<double>(f);
Если мы явно передаём в список параметров значения с параметрами шаблона, то указывать специализацию (..< double >) не нужно. Функция может иметь параметры по умолчанию того же типа что и шаблон – тогда специализация нужна. Кроме того, специализацию можно указывать КОНСТАНТНОЙ переменной – написать const double P и писать length< P >.
template<typename T> T inc(T &i, T di)
{
return i += di;
}
int i=0;
inc(i,5);
double d=3.2;
inc(d,1.1);
Более того – можно в ШАБЛОНЕ задавать параметры по умолчанию, <typename T=int>
При работе с классами процедура аналогична. В данном случае шаблон НЕ ЕСТЬ класс – по шаблону мы будем создавать какой-либо класс.
template <typename T1, typename T2>
class A //<int, double> - конкретный класс, <int, T2> - шаблон с частичной специализацией
{
/*...*/
};
///Примеры частичных специализаций:
template <typename T>
class A<T,T>
{};
template <typename T1, typename T2>
class A<T1*,T2*> //если при создании класса будем специализировать указатель адресами, то будет использоваться этот шаблон, а не верхний
{};
В случае функций – мы получали перегруженные функции. Здесь мы получаем совершенно разные классы. Параметры-значения можно инициализировать константами (целого типа) или указателями на объекты с внешним связыванием (определенные вне данного файла).
//Использование специализации при определении:
template <typename T, size_t Size> //сайз – константа //пример того чего не надо делать
class Vector
{
T v[Size];
/*...*/
};
//при определении обязательно специализировать значение параметра
//частичной специализации быть не может - должен создаться объект конкретного класса
Vector<double,10> V1;
Vector<double,20> V2;
Два объекта разных классов. Что не есть торт. Если использование шаблона идёт локально то всё нормально, но если его передавать куда-то – то методы других классов должны быть опять-таки шаблонными. Таким образом, пока мы не создадим чего-нибудь, мы не сможем проверить. Ошибки в шаблонах могут сидеть чрезвычайно долго. Шаблоны хороши только при локальном использовании.
Допускается выделять базовый класс вне шаблона; шаблон же будет производной от него. На уровне же шаблона необходимо будет реализовывать только операции для передачи данного, транзитные операции (данное принимается, куда-то передаётся, а там где начинаем работать – работаем уже конкретное приведение типа со специализацией). Таким образом решается проблема классов-посредников.
class A {};
template <typename T> //использование в шаблоне базового класса
class B: public A
{
public:
int f();
};
template<typename T>
int B<T>::f()
{
}
Методы шаблонного класса являются опять-таки шаблонами; определять их надо как шаблоны, но можно указывать специализацию – полную или частичную – под конкретные значения параметры.