вернуться в оглавление предыдущая глава предыдущий параграф следующий параграф следующая глава


6.3. Шаблоны функций и классов

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

Шаблоны (templates) – средство для реализаций параметризированных классов и функций на языке С++.

6.3.1. Шаблоны функций. Пример 6.2 (шаблон функции)

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

void swap(int& x, int& y) 
{ 
    int temp=x; 
    x=y; 
    y=temp; 
}
template<class TYPE> 
void swap(TYPE& x, TYPE& y) 
{ 
    TYPE temp=x; 
    x=y; 
    y=temp; 
}

Для создания шаблона мы заменили тип int на лексему TYPE с помощью ключевого слова template в скобках <>. Компилятор же выполнит замену наоборот, он заменит лексему TYPE на любой тип, который ему указать:

// использование шаблона
double x=1.5, y=1.6;
swap<double>(x,y); // в данном случае тип double 

Генерация функции по шаблону и ее аргументу называется инстанцирование. Возможно выведение типа аргумента шаблона по типам его аргументов, но лучше явно указывать параметр в скобках <>.

/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 6.2. Шаблон функции
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <string>
using namespace std;

/////////////////////////////////////////////////////////////////////////////
// шаблон функции, меняющий местами значения двух переменных
// TYPE - параметр шаблона (тип данных переменных)
template<class TYPE>
void Swap(TYPE& x, TYPE& y)
{
    TYPE temp=x; 
    x=y; 
    y=temp;
}
/////////////////////////////////////////////////////////////////////////////
// тестирование шаблона функции
void main()
{
    double dx=1.5, dy=1.6;
    cout<<dx<<" "<<dy<<endl;
    Swap<double>(dx, dy); // TYPE заменяется на double 
    cout<<dx<<" "<<dy<<endl;

    int ix=1, iy=5;
    cout<<ix<<" "<<iy<<endl;
    Swap<int>(ix, iy);    // TYPE заменяется на int 
    cout<<ix<<" "<<iy<<endl;

    string sx="one", sy="two";
    cout<<sx<<" "<<sy<<endl;
    Swap<string>(sx, sy); // TYPE заменяется на string 
    cout<<sx<<" "<<sy<<endl<<endl;
}

6.3.2. Шаблоны функций с несколькими параметрами. Пример 6.3 (шаблон функции с несколькими параметрами)

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

/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 6.3. Шаблоны функций с несколькими параметрами
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std;

/////////////////////////////////////////////////////////////////////////////
// шаблон функции с двумя параметрами TYPE1 и TYPE2
// преобразование переменно x типа TYPE1 к типу TYPE2
template<class TYPE1, class TYPE2>
bool transform(TYPE1 x, TYPE2& y)
{
    if(sizeof(y) < sizeof(x))
        return false;
    y=(TYPE2)x;
    return true;
}
/////////////////////////////////////////////////////////////////////////////
// шаблон функции с двумя параметрами: типом и числовым значением
// функция вычисляет факториал числа n
template<class TYPE, int n>
TYPE factorial()
{
    TYPE sum=1;
    int i=1;
    while(i<=n)
    {
        sum*=i;
        i++;
    }
    return sum;
}
/////////////////////////////////////////////////////////////////////////////
// тестирование шаблона функции с несколькими параметрами
void main()
{
    int x=1;
    double y;

    // преобразование типа int в double
    transform<int, double>(x, y);
    cout<<"double("<<x<<")="<<y<<endl<<endl;

    double res=factorial<double, 4>();
    cout<<"4!="<<res<<endl;
}
/////////////////////////////////////////////////////////////////////////////

6.3.3. Шаблоны классов. Пример 6.4 (шаблон класса Комплексное число)

Шаблоны позволяют реализуют в языке С++ такое отношение между классами, как инстанцирование. При создании шаблона класса тип одного или нескольких его переменных членов задаются в качестве параметра. При использовании этого шаблона необходимо будет указать, какой тип использовать в качестве параметра. Это и называется инстанцированием. Например, обобщенный класс Complex. Многие классы, которые используются для хранения и управления гомогенными структурами данных, реализуют в виде шаблонов. И действительно, независимо от типов хранимых в нем элементов, класс Complex должен выполнять одни и те же функции

Процесс генерации объявления класса по шаблону и аргументу называется инстанцированием шаблона.

/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 6.4. Шаблон класса Комплексное число
// complex.h
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
// проверка на повторное подключение файла
#if !defined COMPLEX_H
#define COMPLEX_H 

#include <iostream>
using namespace std;
/////////////////////////////////////////////////////////////////////////////
// шаблон класса Комплексное число
template <class PAR>
class Complex
{
private:
    // вещественная и мнимая часть комплексного числа
    PAR m_re, m_im;

public:
    // конструкторы
    Complex<PAR>();
    Complex<PAR>(PAR re, PAR im=PAR(0));
    Complex<PAR>(const Complex<PAR>& other); 

public: 
    // получение параметров комплексного числа
    PAR GetRe() const;
    PAR GetIm() const;
    // изменение параметров комплексного числа
    void Set(PAR re, PAR im=PAR(0));

    // оператор умножения
    Complex<PAR> operator*(const Complex<PAR>& other) const;
    // оператор умножения на число
    Complex<PAR> operator*(const PAR& other) const;
    // оператор умножения с присваиванием
    Complex<PAR>& operator*=(const Complex<PAR>& other);
    // оператор присваивания
    Complex<PAR>& operator=(const Complex<PAR>& other);
    // оператор равенства
    bool operator== (const Complex<PAR>& other) const;
    // оператор сопряжения комплексного числа
    Complex<PAR> operator~() const;
    // унарный минус
    Complex<PAR> operator-() const;

    // ввод/вывод комплексного числа
    template <class PAR>
    friend ostream& operator<< (ostream& out, const Complex<PAR>& x);
    template <class PAR>
    friend istream& operator>> (istream& out, Complex<PAR>& x);
};
/////////////////////////////////////////////////////////////////////////////
// конструктор по умолчанию
template <class PAR>
Complex<PAR>::Complex()
    : m_re(0.)
    , m_im(0.)
{    
}
/////////////////////////////////////////////////////////////////////////////
// полный конструктор
template <class PAR>
Complex<PAR>::Complex(PAR re, PAR im)
    : m_re(re)
    , m_im(im)
{
}
/////////////////////////////////////////////////////////////////////////////
// конструктор копирования
template <class PAR>
Complex<PAR>::Complex(const Complex<PAR>& x) 
    : m_re(x.m_re)
    , m_im(x.m_im)
{
}
////////////////////////////////////////////////////////////////////////////
// получение вещественной части комплексного числа 
template <class PAR>
PAR Complex<PAR>::GetRe() const
{
    return m_re;
}
/////////////////////////////////////////////////////////////////////////////
// получение мнимой части комплексного числа
template <class PAR>
PAR Complex<PAR>::GetIm() const
{
    return m_im;
}
/////////////////////////////////////////////////////////////////////////////
// изменение параметров комплексного числа
template <class PAR>
void Complex<PAR>::Set(PAR re, PAR im)
{
    m_re=re;
    m_im=im;
}
/////////////////////////////////////////////////////////////////////////////
// оператор умножения
template <class PAR>
Complex<PAR> Complex<PAR>::operator*(const Complex<PAR>& other) const
{
    return Complex<PAR>(m_re*other.m_re-m_im*other.m_im,  
                        m_re*other.m_im-m_im*other.m_re);
}
/////////////////////////////////////////////////////////////////////////////
// оператор умножения на число
template <class PAR>
Complex<PAR> Complex<PAR>::operator*(const PAR& other) const
{
    return Complex<PAR>(m_re*other, m_im*other);
} 
/////////////////////////////////////////////////////////////////////////////
// оператор умножения с присваиванием
template <class PAR>
Complex<PAR>& Complex<PAR>::operator*=(const Complex<PAR>& other)
{
    Complex temp(*this);
    m_re=temp.m_re*other.m_re - temp.m_im*other.m_im;
    m_im=temp.m_re*other.m_im + temp.m_im*other.m_re;
    return (*this);
} 
/////////////////////////////////////////////////////////////////////////////
// унарный минус
template <class PAR>
Complex<PAR> Complex<PAR>::operator-() const
{
    return Complex<PAR>(-m_re, -m_im);
}
/////////////////////////////////////////////////////////////////////////////
// оператор сопряжения комплексного числа
template <class PAR>
Complex<PAR> Complex<PAR>::operator~() const
{
    return Complex<PAR>(m_re, -m_im);
} 
/////////////////////////////////////////////////////////////////////////////
// оператор равенства
template <class PAR>
bool Complex<PAR>::operator== (const Complex<PAR>& other) const
{
    return (m_re == other.m_re && m_im == other.m_im);
} 
/////////////////////////////////////////////////////////////////////////////
// оператор присваивания
template <class PAR>
Complex<PAR>& Complex<PAR>::operator=(const Complex<PAR>& other)
{
    if(this != &other)
    {
        m_re=other.m_re;
        m_im=other.m_im;
    }
    return *this;
}
/////////////////////////////////////////////////////////////////////////////
// вывод комплексного числа на экран
template <class PAR>
ostream& operator<< (ostream& out, const Complex<PAR>& x)
{
    return (out<<"("<<x.m_re<<","<<x.m_im<<")");
}
/////////////////////////////////////////////////////////////////////////////
// ввод комплексного числа с клавиатуры
template <class PAR>
istream& operator>> (istream& in, Complex<PAR>& x) 
{
    return (in>>x.m_re>>x.m_im);
}
/////////////////////////////////////////////////////////////////////////////
#endif //defined Complex_H

 

/////////////////////////////////////////////////////////////////////////////
// Прикладное программирование
// Пример 6.4. Шаблон класса Комплексное число
// test_complex.cpp
// 
// Кафедра Прикладной и компьютерной оптики, http://aco.ifmo.ru
// Университет ИТМО
/////////////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std;

// подключение описания класса
#include "complex.h"
/////////////////////////////////////////////////////////////////////////////
// пример использования шаблона класса Complex
void main()
{
    Complex<int> a(5), b(3,3);
    Complex<double> c(1.144, -0.155);

    cout<<a<<" "<<b<<" "<<c<<endl; 
}
/////////////////////////////////////////////////////////////////////////////