images.jpg

 

 

Попередня сторінка          Зміст           Наступна сторінка          Електронні посібники ВНТУ

 

 

 

 

ЛАБОРАТОРНА РОБОТА № 14

ШАБЛОНИ ФУНКЦІЙ І КЛАСІВ. ПАРАМЕТРИЗОВАНІ КОНТЕЙНЕРНІ КЛАСИ БІБЛІОТЕКИ STL

images (51).jpg
 

Мета роботи

  • Ознайомитися із базовими механізмами використання шаблонів функцій.
  • Навчитись створювати та використовувати шаблонів класів.
  • Засвоїти правила створення та використання параметризованих контей­нерних класів.
  • Дослідити основні класи з бібліотеки STL.

 

 
 images (47).jpg
        

       ТЕОРЕТИЧНІ ВІДОМОСТІ

 

Шаблони функцій

Шаблони функцій – це потужний інструмент у С++, який суттєво спрощує роботу програміста. Наприклад, нам потрібно запрограмувати функцію, яка виводила б на екран елементи масиву. При цьому ми хочемо, щоб функція виводила масиви типу int, double, float і char. Тобто, нам потрібно запрограмувати 4 функції, які виконують одні й ті самі дії, але для різних типів даних. Скористаємося перевантаженням функцій.

 

void printArray(const int * array, int count)

{   for (int ix = 0; ix < count; ix++)

        cout << array[ix] << "   ";

    cout << endl;

}

void printArray(const double * array, int count)

{   for (int ix = 0; ix < count; ix++)

        cout << array[ix] << "   ";

    cout << endl;

}

void printArray(const float * array, int count)

{   for (int ix = 0; ix < count; ix++)

        cout << array[ix] << "   ";

    cout << endl;

}

void printArray(const char * array, int count)

{   for (int ix = 0; ix < count; ix++)

        cout << array[ix] << "   ";

    cout << endl;

}

 

Таким чином, ми маємо 4 перевантажені функції, для різних типів даних. Ввони відрізняються тільки заголовком, тіло у них абсолютно однакове.

А що якщо, нам знадобиться запрограмувати алгоритм сортування у вигляді функції. Виходить, для кожного типу даних доведеться свою функцію створювати. Тобто, один і той же код буде в декількох примірниках, що дуже нераціонально. Тому в С ++ придуманий такий механізм – шаблони функцій.

Шаблони функцій – це інструкції, згідно з якими створюються локальні версії функції для певного набору параметрів і типів даних.

Синтаксис:

template <class T>

template <typename T>

template <typename T1, typename T2>

Всі шаблони функцій починаються зі слова template, після якого йдуть кутові дужки, в яких перераховується список параметрів. Кожному параметру має передувати зарезервоване слово class або typename.

Ключове слово typename говорить про те, що у шаблоні буде використовуватися вбудований тип даних, такий як: int, double, float, char і т. д. А ключове слово class повідомляє компілятору, що в шаблоні функції як параметр будуть використовуватися типи даних користувача, тобто класи.

Ми створюємо один шаблон, в якому описуємо всі типи даних. Таким чином код не буде захаращуватися.

Приклад 1. Програма виводить на екран елементи масивів різних типів, використовуючи шаблон функції.

#include <iostream>

using namespace std;

    // шаблон функції printArray

template <typename T>

void printArray(const T * array, int count)

{   for (int ix = 0; ix < count; ix++)

        cout << array[ix] << "   ";

    cout << endl;

}

int main()

{   const int iSize = 10, dSize = 7, fSize = 10, cSize = 5;

         // масиви різних типів даних

    int    iArray[iSize]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    double dArray[dSize]={1.2345,2.234,3.57,4.67876,5.346,6.1545,7.7682};

    float  fArray[fSize]={1.34,2.37,3.23,4.8,5.879,6.345,73.434,8.82,9.33,10.4};

    char   cArray[cSize] = {"MARS"};

    cout << "\n\t Використання шаблонiв функцiй:\n";

    cout << "\n\tМасив типу int:\n\t";  printArray(iArray, iSize);

    cout << "\n\tМасив типу double:\n\t"; printArray(dArray, dSize);

    cout << "\n\tМасив типу float:\n\t"; printArray(fArray, fSize);

    cout << "\n\tМасив типу char:\n\t"; printArray(cArray, cSize);

    system("pause");

    return 0;

}

Результат роботи програми:

 

 

 

Шаблони класів

У міру того, як кількість створюваних класів зростає, виявляється, що деякий клас, створений для однієї програми, необхідний в іншій програмі. У багатьох випадках класи можуть відрізнятися тільки типами. Наприклад, один клас працює з цілочисельними значеннями, в той час як в іншій програмі він повинен працювати зі значеннями типу float.

Щоб збільшити ймовірність повторного використання існуючого коду, C++ дозволяє програмам визначати шаблони класів.

Шаблон класу визначає тіпонезалежний клас, який надалі служить для створення об'єктів необхідних типів.

Якщо компілятор C++ зустрічає оголошення об'єкта, засноване на шаблоні класу, то для побудови класу необхідного типу він буде використовувати типи, зазначені при оголошенні. Дозволяючи швидко створювати класи, що відрізняються тільки типом, шаблони класів скорочують обсяг програмування, що, в свою чергу, заощаджує час.      

Пояснимо використання шаблонів класів на простому прикладі. Нехай необхідно створити простий клас "Масив", який буде виконувати прості дії: Додавання і Відображення елементів.

Приклад. Створення класу "Масив" без шаблону.

#include <iostream>

using namespace std;

         /*НАШ КЛАСС*/

 class Massiv

 {

    int Array[10];    //Масив цiлочислених значень з 10 елементiв

    int count;        //Лiчильник елементiв масиву

   public:

    void Add(int );   //Метод для додавання елементiв в масив

    void Show();      //Метод для вiдображення масиву на екранi

 };

 void Massiv::Add(int x)

 {

   static int pos=0;

   Array[pos]=x;

   pos++;

   count=pos;

 }

 void Massiv::Show()

 {

  for (int i=0;i<count;i++)

      cout<<Array[i]<<"\t";

  cout<<endl;

 }

int main()

{

  Massiv Arr; 

  Arr.Add(100.555);

  Arr.Add(200);

  Arr.Add(300);

  Arr.Show();

  system("pause");

  return 0;

}

Це приклад створення звичайного класу. Але у програмістів іноді виникає необхідність створення такого ж класу, в якому відрізняється тільки тип даних. Наприклад, може знадобитися створення класу, в якому потрібно створення масиву, який буде зберігати в собі і обробляти не цілочисельні змінні, а рядкові. Як варіант, можна дописати купу класів для кожного з типів змінних, але це не раціонально. Код вийде більшим, громіздким. Чим більше коду, тим простіше в ньому помилятися і тим складніше шукати. Ось тут і приходять на допомогу шаблони класів.

Приклад 2. Створення класу "Масив" з використанням шаблону.

 

#include <iostream>

using namespace std;

int pos=0; //Позицiя в масивi

template <class T> //Шаблон с класу з параметром T

 class Massiv

 {

    T Array[10];      //Масив цiлочислених значень з 10 елементiв

    int count;        //Лiчильник елементiв масиву

   public:

    void Add(T ); //Метод для додавання елементiв в масив

    void Show();      //Метод для вiдображення масиву на екранi

 };

 template <class T> void Massiv<T>::Add(T x)

 { static int pos=0;

   Array[pos]=x;

   pos++;

   count=pos;

 }

 template <class T> void Massiv<T>::Show()

 {  cout<<"\t"<<endl;

    for (int i=0;i<count;i++)

         cout<<"\t"<<Array[i];

    cout<<endl;

 }

int main()

{

  setlocale(0,"");

  Massiv<int> Arr; 

  Arr.Add(100.555);

  Arr.Add(200);

  Arr.Add(300);

  Arr.Show();

  Massiv<char *> Arr2;

  Arr2.Add("Строка");

  Arr2.Add("Начинаю понимать");

  Arr2.Add("УРА");

  Arr2.Show();

  cout<<endl;

  system("pause");

  return 0;

}

                   Результат роботи програми:

 

Стандартна бібліотека шаблонів  STL

Стандартна бібліотека шаблонів надає набір добре сконструйованих узагальнених компонентів C++. Бібліотека містить п'ять основних видів компонентів:

  1. алгоритм (algorithm) – визначає обчислювальну процедуру;
  2. контейнер (container) – управляє набором об'єктів в пам'яті;
  3. ітератор (iterator) – забезпечує для алгоритмів засіб доступу до вмісту контейнера;
  4. функціональний об'єкт (function object) – інкапсулює функцію в об'єкті для використання іншими компонентами;
  5. адаптер (adaptor) – адаптує компонент для забезпечення різного інтерфейсу.

До найпопулярніших контейнерів відносять такі:

vector – колекція елементів, збережених в масиві, що збільшується в міру необхідності.  Заголовочний файл – <vector>.

list – колекція елементів, збережених, як двонаправлений зв'язаний список. Заголовочний файл – <list>.

map – це колекція, яка зберігає пари значень pair <Key,T> і призначена для швидкого пошуку значення за ключем Key. Як ключ може бути використано, наприклад, рядок або int, але при цьому необхідно пам'ятати, що головною особливістю ключа є можливість застосувати до нього операцію порівняння. Важливо: ключ повинен бути унікальним. Заголовочний файл – <map>.

set – це колекція унікальних значень Key – кожне з яких є також і ключем. Тобто, це відсортована колекція, призначена для швидкого пошуку необ­хід­ного значення. До ключа пред'являються ті ж вимоги, що й у випадку ключа для map. Природно, використовувати її для цієї мети немає сенсу, якщо зберігати в ній прості типи даних, щонайменше необхідно визначити свій клас, який зберігає пару ключ – значення і визначає операцію порівняння по ключу. Дуже зручно використовувати дану колекцію, якщо треба уникнути повторного збереження одного і того ж значення. Заголовочний файл – <set>.

multimap – це модифікований map, в якому відсутня вимоги унікальності ключа Тобто, якщо здійснювати пошук по ключу, то повернеться не одне значення, а набір значень, збережених з даними ключем. Заголовочний файл – <map>.

multiset – те ж саме відноситься і до цієї колекції, вимоги унікальності ключа в ній не існує, що призводить до можливості зберігання дублікатів значень. Тим не менш, існує можливість швидкого знаходження значень по ключу у випадку, якщо визначити свій клас. Оскільки всі значення в map і set збері­гаються у відсортованому вигляді, то в цих колекціях можна швидко відшу­кати необхідне значення за ключем, але при цьому операція вставляння нового елемента буде складнішою, ніж, наприклад, в vector. Заголовочний файл – <set>.

Приклад 3. Використання контейнера vector з STL для цілих чисел.

 

#include <vector>

#include <algorithm>

#include <iostream>

using namespace std;

template <class T> void pr(T& v)

{         //===== Шаблон функції для виведення за допомогою ітератора       

    T::iterator p;    //====== Ітератор для будь-якого контейнера

    for ( p=v.begin(),int i=0; p!=v.end(); p++, i++)

          cout << endl << i + 1 <<". "<< *p;

    cout << '\n';

}

void main ()

{   vector <int> v(10);     //======== Вектор цілих

    cout << "\nInt Vector:\n";

    for (int i=0; i<v.size(); i++)

    {     v[i] = rand()%10 + 1;

         cout << v[i]<< "; ";

    }

    sort (v.begin (), v.end());   //======= Сортування за замовчуванням

    cout << "\n\nAfter default sort\n";

    for (int i=0; i<v.size(); i++) cout << v[i]<< "; ";

    cout << "\n\nUsing iterators\n\n";

    pr(v);

    v.erase(v.begin());     //======== Видалення елементів

    cout << "\n\nAfter first element erasure\n";

    for (int i=0; i<v.size(); i++) cout << v[i]<< "; ";

    v.erase (v.end()-2, v.end());

    cout << "\n\nAfter last 2 elements erasure\n";

    for (int i=0; i<v.size(); i++) cout << v[i]<< "; ";

    int size = 2;           //======== Зміна розмірів

    v.resize(size);

    cout << "\n\nAfter resize, the new size: " << v.size()<< endl;

    for (uint i=0; i<v.size(); i++) cout << v[i]<< "; ";

    v.resize(6,-1);

    cout << "\n\nAfter resize, the new size: " << v.size()<< endl;

    for (uint i=0; i<v.size(); i++) cout << v[i]<< "; ";

    cout << "\n\nVector's maximum size: " << v.max_size()

          << "\nVector's capacity: " << v.capacity() << endl;

    v.reserve (100);

    cout << "\nAfter reserving storage for 100 elements:\n"<< "Size: "

         <<v.size()<<endl<<"Maximum size: "<<v.max_size()<< endl

         << "Capacity: " << v.capacity() << endl;

    v.resize(2000);

    cout << "\nAfter resizing storage to 2000 elements:\n"<<"Size: "<<v.size()

         <<endl<<"Maximum size: "<<v.max_size() << endl<<"Capacity: "

         <<v.capacity()<<endl<< "\n\n";

    _getch();

}

Результат роботи програми:

    

 

 

 

 

 

 

 

 

 

 

 

 

images (37).jpg

 

Порядок виконання роботи

 

 

  1. Засвоїти теоретичний матеріал. Використовуючи лекційний матеріал і інші інформаційні джерела, навчитись створювати шаблони функцій і класів.
  2. Розібратись у наведених прикладах і здійснити компіляцію програм з прикладів 1,2,3.
  3. Розробити програми згідно індивідуального завдання.
  4. Підготувати звіт з лабораторної роботи, у якому представити такі матеріали:
    •  зміст завдання і варіант;
    •  лістинг програми;
    •  результати роботи розробленої програми;
    •  висновки.

 

images (60).jpg

 

Варіанти індивідуальних завдань

 

Створити клас, який описує та забезпечує дії над даними пара­мет­ри­зова­но­го масиву, розмірність якого визначається під час роботи програми. Усі об­чис­лення і перетворення реалізувати у вигляді функцій-членів класу.

 

1 В масиві обчислити: 
    – номер елемента масиву, найближчого до середньоарифметичного його значень; 
    – різниця елементів масиву, що розташовані між першим від'ємним та другим додатним елементами. 
Перетворити масив так, щоб у його першій половині розташовувались елементи, що стоять в парних позиціях, а в другій – елементи, що стоять в непарних позиціях.
2 Дана прямокутна матриця. Визначити: 
    – кількість від'ємних елементів в тих рядках, які містять хоча б один нульовий елемент; 
    – суму модулів елементів, які розташовані після першого додатного елемента 
Впорядкувати елементи матриці за спаданням модулів елементів
3 У довільній матриці обчислити: 
    – кількість елементів масиву, рівних нулю; 
    – суму елементів масиву, які лежать в діапазоні від А до В. 
Впорядкувати елементи масиву за спаданням модулів елементів
4 У довільній матриці обчислити: 
    – кількість елементів масиву, рівних нулю; 
    – суму елементів масиву, які лежать в діапазоні від А до В. 
Впорядкувати елементи масиву за спаданням модулів елементів
5 В одновимірному масиві елементів, обчислити: 
    – номер максимального за модулем елемента; 
    – суму модулів елементів, які розташовані після першого додатного елемента. 
Перетворити масив таким чином, щоб спочатку розташовувались всі елементи, ціла частина яких лежить в інтервалі [a,b], а потім – всі інші
6 В масиві обчислити: 
    – мінімальний за модулем елемент масиву; 
    – суму модулів елементів, які розташовані після першого від'ємного елемента. 
Ущільнити масив, видаливши з нього елементи, величина яких знаходиться на інтервалі [a,b]. Місце, яке звільниться в кінці масиву заповнити символом чи числом з клавіатури.
7 В масиві обчислити: 
    – мінімальний за модулем елемент масиву; 
    – суму модулів елементів масиву, розташованих після першого нільового елемента. 
Перетворити масив так, щоб в першій його половині розташовувались елементи, що стоять на парних позиціях, а в другій – елементи, що стоять в непарних позиціях.
8 У матриці обчислити: 
    – максимальний за модулем елемент масиву; 
    – суму елементів масиву, що розташовані між першим і другим додатними елементами. 
Перетворити матрицю так, щоб всі елементи, рівні нулю, розташовувались в кінці.
9 Дана прямокутна матриця. Визначити: 
    – кількість рядків, які не містять жодного нульового елемента; 
    – максимальне із чисел, що зустрічається в заданій матриці більше одного разу
Перетворити матрицю так, щоб всі нульові елементи розташовувались на початку.
10 Дана прямокутна матриця. Визначити:
    – кількість стовпців, які не містять жодного нульового елемента. 
    – кількість елементів, менших за А , але більших за В. 
Переставляючи рядки заданої матриці, розташувати їх у відповідності із зростанням суми значень у стовпцях.
11 В одномірному масиві обчислити: 
    – добуток елементів масиву з парними номерами; 
    – суму елементів масиву, які розташовані між першим і останнім нульовими елементами. 
Впорядкувати масив таким чином, щоб спочатку розташовувались всі додатні елементи, а потім – всі від'ємні (елементи, рівні 0 вважати додатними).
12 Дана прямокутна матриця. Визначити : 
    – кількість стовпців, які містять хоча б один нульовий елемент; 
    – номер рядка, в якому знаходиться найдовша серія з однакових елементів. 
Впорядкувати масив таким чином, щоб спочатку розташовувались всі серії з однакових елементів, а потім – всі решта елементів.
13 В одномірному масиві, що складається з N дійсних елементів, обчислити: 
    – суму елементів масиву з непарними елементами; 
    – суму елементів масиву, які розташовані між першим і останнім від'ємними елементами. 
Перетворити масив, видаливши з нього всі елементи, модуль яких не перевищує число, що вводиться з клавіатури. Елементи, які звільняться в кінці масиву заповнити нулями.
14 Дана прямокутна матриця. Визначити : 
    – добуток елементів в тих рядках, які не містять від'ємних елементів; 
    – максимум серед сум елементів діагоналей, паралельних головній діагоналі матриці. 
Перетворити матрицю, видаливши з неї всі елементи, модуль яких не перевищує число, що вводиться з клавіатури. Елементи, які звільняться в кінці масиву, заповнити нулями.
15 В одномірному масиві, що складається з N дійсних елементів, обчислити: 
    – максимальний елемент масиву; 
    – суму елементів масиву, що розташовані до останнього додатного елемента. 
Перетворити масив, видаливши з нього всі елементи, модуль яких знаходиться в інтервалі [a,b]. Елементи, які звільняються в кінці масиву заповнити нулями.

 

ask5-1

 

Контрольні питання

 

 

  1. Яка різниця між шаблоном та макросом?
  2. Чим відрізняється параметр шаблона від параметру функції?
  3. Як створити шаблонний клас?
  4. Що являє собою стандартна бібліотека шаблонів STL?
  5. Які основні шаблонні класи колекцій ви знаєте?
  6. Що представляють собою ітератори?