Функции-шаблоны (родовые функции) 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Функции-шаблоны (родовые функции)



 

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

Определение имеет формат

template   < class X [, class Y,…]> имя_функции

  {определение_функции}

Причем вместо типа какого-либо аргумента (или всех) или возвращаемого значения может стоять X, Y,…, где X, Y,… – идентификаторы для обозначения произвольного типа данных.

Рассмотрим такую задачу: отсортировать массив целых чисел и строк методом пузырька.

Вообще говоря, потребовалось бы создать 2 функции для сортировки каждого массива и 2 для обмена двух значений, т.е. 4 функции. Вместо этого напишем 2 функции-шаблона. Определим дополнительно еще 2 функции шаблона – для нахождения max элемента в массиве данных и вывода на экран двух значений данных разного типа.

 

enum   boolean {FALSE, TRUE};

#include   "string.hpp"

#include   "complex.hpp"

template  <class T>  inline  void  Swap(T & a, T & b)

{T c;

 c = a, a = b, b = c;

 }

 

 template <class T> T  Max(T * a, int  n)

 {int  i;T max = a[0];

  for(i = 1; i < n; i++)

   if (a[i] > max) max = a[i];

  return max;

 }

 

template <class T>  void  Bul_bul(T * a, int  n) // улучшенный «пузырек» с флагом f

 

{int i, j, f;

for(i = 0, f = 1; i < n – 1 && f; i++)

  for(j = f = 0; j < n – i – 1; j++)

     if (a[j] > a[j + 1]) {Swap(a[j], a[j + 1]); f = 1;}

}

 

template <class X, class Y>  void  out2(X a, Y b)

{cout << a << ' ' << b; }

 

Как же будет работать компилятор?

При вызове функций-шаблонов в зависимости от типов фактических аргументов компилятор создает различные версии каждой из этих функций. Говорят – компилятор создает порожденную функцию. Процесс генерации порожденной функции называют созданием экземпляра функции. Таким образом, порожденная функция – это конкретный экземпляр функции-шаблона.

 

void  main()

{ Complex b[5] = {Complex(4, 2), Complex(-3, 4), 1, -2};

String s[5] = {"Петров", "Сибирякова", "Горцев", "Савин", "Буторина"};

int a[10] = {5, 3, 9, 6, 2, 0, 1152, 7, 4}, i;

cout << "\n Мax среди строк " << Max(s, 5);      // Генерируется экземпляр String Max(String*, int)

cout << "\n Мax среди целых чисел " << Max(a, 9); // Генерируется экземпляр int Max(int *, int)

Bul_bul(s, 5);    // Генерируется экземпляр void Bul_bul(String *, int);

cout << "\nОтсортированные строки ";

for(i = 0; i < 5; i++)

  out2(s[i], ' ');  // генерируется экземпляр void out2(String, char);

Bul_bul(a, 9);   // генерируется экземпляр void Bul_bul(int *, int);

cout << "\nОтсортированные целые числа\n";

for(i = 0; i < 9; i++)

   out2(a[i], ' ');  // генерируется экземпляр void out2(int, char);

cout << "\nКомплексные числа\n";

for(i = 0; i < 5; i++)

  out2(b[i], " "); // генерируется экземпляр void out2(Complex, char *)

 out2(endl, "*********"); // генерируется экземпляр void out2(char, char *)

 getch();

}

 

При порождении экземпляров функции-шаблона сортировки Bul_bul(...) компилятор сгенерирует также 2 функции Swap –

Swap (String &, String &) и Swap(int &, int &).

 

Программа будет работать при выполнении следующих условий:

1)  в классе String должна быть перегружена операции сравнения на >;

2)  в классах String и Complex должна быть перегружена операция потокового вывода <<;

3)  в классе String, кроме того, должна быть перегружена операция =.

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

Классы-шаблоны

Так же, как и функции-шаблоны, могут быть определены и классы-шаблоны.

Определение. Класс-шаблон – это класс, в котором одни и те же член-данные могут быть разных типов, а действия над ними – одинаковые. Формат определения класса-шаблона

template   < class X, class Y,...> class имя_класса

{ X определение  член-данных;

Y определение  член-данных;

.......

};

Здесь X и Y – условное обозначение типов данных.

Использование такого класса в функциях следующее:

имя_класса  <конкретные_типы>  имя_объекта[(арг-ты  констр-а)];

При компиляции для каждого списка типов данных, указанного в < >, будет создан свой экземпляр класса.

Рассмотрим пример класса-шаблона Stack. В стек кладутся целые и комплексные числа, строки.

 

enum  boolean {FALSE, TRUE};

#include "string.hpp"

#include "Complex.hpp"

template   <class T>  class Stack

{ T *a; int   max, n;

  public:

  Stack(int  k = 50)

     {a = new T [max = k]; n = 0; }

 ~Stack()   {delete [ ]a;}          // деструктор

  Stack(Stack<T> &);                // конструктор копирования

   Stack <T> operator = (Stack <T>);

  void  Push(T  b)                      // положить в стек элемент b

      {if  (Full())

        {cout << "Стек переполнен!"; return;}

      a[n++] = b;

   }

T Pop()                           // удалить элемент из стека

    {if  (!isEmpty())    return a[--n];}

T Top()                            // взять значение с вершины стека

  {return a[n - 1];}

boolean   isEmpty()         // стек пуст?

  {if (n = = 0)  return TRUE;

       return FALSE;

   }

boolean Full();                 // стек заполнен?

friend   ostream & operator << (ostream &, Stack  <T> &); // потоковый вывод

};

 

template <class T> Stack< T>:: Stack(Stack<T> & b)

{int  i;

max = b.max; n = b.n;

a= new T[max];

for(i = 0; i < n; i++)  a[i] = b.a[i];

  }

 

template   <class T>   boolean  Stack<T>:: Full()

{ if (n = = max)   return TRUE;  // да, заполнен

  return FALSE;                         // нет, не заполнен

}

 

template   <class T>  ostream &   operator << (ostream & r, Stack <T>  & b)

{ int  i;

 if (b.isEmpty()) {r << "\nстек пуст"; return r;}

 for(i = 0; i < b.n; i++) cout << ' ' << b.a[i];

 return r;

  }

 

 template  <class T>  Stack<T> Stack<T>:: operator = (Stack<T> b)

  {if (& b!= this)

{delete []a;

 a = new T[max = b.max]; n = b.n;

   for(int  i = 0; i < n; i++) a[i] = b.a[i];

  return  *this;

} }

 

void  main()

{Stack  <Complex>  c(5); // cоздается экземпляр стека комплексных чисел

 Stack  <String>  s(5);     // cоздается экземпляр стека строк

 Stack  <int>  a(2), c;       // стек целых чисел

 Stack  <float>  b(3);       // стек вещественных чисел

 String   b4("bbbb");        // строка “bbbb”

 int  i;

 c.Push(Complex(2, 3)); // компл. число 2 + 3 * i => в стек комплексных чисел

 c.Push(3.5);                   // комп. число 3.5 + 3.5 * i => в стек комплексных чисел

 s. Push(«dddddd»);       // строка “dddddd” => в стек строк

 s.Push(b4);                    // строка “bbbb” => в стек строк

 a.Push(77);                    // число 77 => в стек целых чисел

 a.Push(9);                      // число 9 => в стек целых чисел

 a.Push(-10);                  // Сообщение «Стек переполнен!”

 cout << "\n Стек комплексных чисел ";

 cout << c; // работают 2 перегруженные операции потокового вывода << для классов Stack и Complex

 cout<<"\n Стек строк: ";

 while (!s.isEmpty()) // пока стек не пуст

 cout << s.Pop();    // работает перегруженная в классе String операция <<

 //Стек строк опустел

 c = a; // перегруж. операция = переписывает стек целых чисел a в c

 cout << "\n Стек целых чисел" << c;

 cout << ”\n В вершине стека число “ << a.Top();

 cout << "\nСтек вещественных чисел b = " << b;

}

 

Замечание. Так как действия над данными в классах-шаблонах одинаковые, а типы данных могут быть произвольные, то такие классы еще называют контейнерными. В настоящее время существует большой набор стандартных классов-шаблонов: стеки, очереди, деревья, строки, упорядоченные массивы и др.



Поделиться:


Последнее изменение этой страницы: 2021-12-07; просмотров: 51; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.224.39.32 (0.014 с.)