67358

Узагальнені класи

Лекция

Информатика, кибернетика и программирование

Окрім визначення узагальнених функцій, можна також визначити узагальнений клас. Для цього створюється клас, у якому визначаються всі використовувані ним алгоритми. При цьому реальний тип оброблюваних у ньому даних задається як параметр при побудові об’єктів цього класу.

Украинкский

2014-09-07

142.5 KB

0 чел.

Лекція № 21

Тема: Узагальнені класи

План

  1.  Поняття про узагальнені класи
  2.  Створення класу з одним узагальненим типом даних
  3.  Створення класу з двома узагальненими типами даних
  4.  Приклад створення узагальненого класу для організації безпечного масиву
  5.  Використання в узагальнених класах аргументів, що не є узагальненими типами

  1.    Поняття про узагальнені класи

    Окрім визначення узагальнених функцій, можна також визначити узагальнений клас. Для цього створюється клас, у якому визначаються всі використовувані ним алгоритми. При цьому реальний тип оброблюваних у ньому даних задається як параметр при побудові об'єктів цього класу.

    Узагальнені класи особливо є корисними тоді, коли в них використовується логіка, яку можна узагальнити. Наприклад, алгоритми, які підтримують функціонування черги цілочисельних значень, також надаються і для підтримки символьних значень. Механізм, який забезпечує підтримку зв'язного списку поштових адрес, також годиться для підтримки зв'язного списку, призначеного для зберігання даних про запчастини до автомобілів. Після свого створення узагальнений клас виконує визначену програмістом операцію (наприклад, підтримку черги або зв'язного списку) для будь-якого типу даних. Компілятор автоматично згенерує коректний тип об'єкта на основі типу, який задається під час його створення.     

   Загальний формат оголошення узагальненого класу має такий вигляд:

template <class tType> class ім'я_класу 

{

     // Тіло класу

};

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

    Створивши узагальнений клас, можна створити його конкретний примірник, використовуючи такий загальний формат:

ім'я_класу <тип> ім'я_об'єкту;

    У цьому записі елемент тип означає ім'я типу даних, які оброблятимуться примірником узагальненого класу. Функції-члени узагальненого класу автоматично є узагальненими. Тому програмісту не потрібно використовувати ключове слово template для безпосереднього визначення їх такими.

  1.  Створення класу з одним узагальненим типом даних

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

Приклад. Демонстрація механізму створення класу з одним узагальненим типом даних

const int size=5;

 

  // Створення узагальненого класу queueClass.

template <class qType> class queueClass

{         qType array[size];

          int sloc, rloc;

    public:

          queueClass() { sloc = rloc = 0; }

          void Get(qType c);

          qType Put(); // Виведення з об'єкта значення

};

   // Занесення об'єкта в чергу.

template <class qType> void queueClass<qType>::Get(qType c)

{     if(sloc>=size)

                  { cout << "Черга заповнена" << endl; return; }

      sloc++;

      array[sloc] = c;

}

   // Вилучення об'єкта з черги.

template <class qType> qType queueClass<qType>::Put()

{     if(rloc == sloc)

                 { cout << "Черга порожня" << endl; return 0; }

      rloc++;

      return array[rloc];

}

void main()

{    queueClass<int> ObjA, ObjB;         // Створюємо дві черги для int-значень.

     ObjA.Get(10);

     ObjB.Get(19);

     ObjA.Get(20);

     ObjB.Get(1);

     cout << ObjA.Put() << " ";

     cout << ObjA.Put() << " ";

     cout << ObjB.Put() << " ";

     cout << ObjB.Put() << endl;

     queueClass<double> ObjC, ObjD;         // Створюємо дві черги для double-значень

     ObjC.Get(10.12);

     ObjD.Get(19.99);

     ObjC.Get(-20.0);

     ObjD.Get(0.986);

     cout << ObjC.Put() << " ";

     cout << ObjC.Put() << " ";

     cout << ObjD.Put() << " ";

     cout << ObjD.Put() << endl;

}

    Внаслідок виконання ця програма відображає на екрані такі результати:

10 20 19 1

10.12 -20 19.99 0.986

    У цьому коді програми оголошення узагальненого класу є подібним до оголошення узагальненої функції. Тип даних, який зберігаються в черзі, узагальнений в оголошенні класу. Він невідомий доти, доки не буде оголошений об'єкт класу queueClass, який і визначить реальний тип даних. Після оголошення конкретного примірника класу queueClass компілятор автоматично згенерує всі функції та змінні, необхідні для оброблення реальних даних. У цьому прикладі оголошуються два різних типи черги: дві черги для зберігання цілих чисел і дві черги для значень типу double. Зверніть особливу увагу на ці оголошення:

queueClass<int> ObjA, ObjB;

queueClass<double> ObjC, ObjD;

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

queueClass<char *> chrptrQ;

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

struct addrStruct

{       // Оголошення типу структури

     char name[40];

     char street[40];

     char city[30];

     char state[3];

     char zip[12];

};

  Тепер для того, щоби за допомогою узагальненого класу queueClass, можна було згенерувати чергу для зберігання об'єктів типу addrStruct, достатньо використовувати таке оголошення:

queueClass<addrStruct> obj;

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

  1.   Створення класу з двома узагальненими типами даних

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

Приклад. Демонстрація механізму створення класу з двома узагальненими типами даних

template <class aType, class bType> class myClass

{           aType c;

            bType d;

     public:

            myClass(aType ObjA, bType ObjB) { c = ObjA; d = ObjB; }

            void showType()

                         {cout << "c= " << c << "; d= " << d << endl; }

};

void main()

{    myClass<int, double> ObjA(10, 0.23);

     myClass<char, char *> ObjB('x', "Це тест.");

     ObjA.showType();           // Відображення int- і double-значень

     ObjB.showType ();          // Відображення значень типу char і char *

}

   Внаслідок виконання ця програма відображає на екрані такі результати:

c= 10; d= 0.23

c= x; d= Це тест.

   У цьому коді програми оголошується два види об'єктів. Об'єкт ObjA використовує дані типу int і double, а об'єкт ObjB – символ і показник на символ. Для цих ситуацій компілятор автоматично генерує дані та функції відповідного способу побудови об'єктів.

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

   Розглянемо ще одне застосування узагальненого класу. Як було показано в попередніх темах, можна перевантажувати оператор "[]", що дає змогу створювати власні реалізації масивів, у тому числі і "безпечні масиви", які забезпечують динамічну перевірку порушення його меж. Як уже зазначалося вище, у процесі виконання програми, написаній мовою програмування C++, можливий вихід за межі масиву без видачі повідомлення про помилку. Але, якщо створити клас, який би містив масив, і дати змогу доступ до цього масиву тільки через перевантажений оператор індексації ("[]"), то можна перехопити індекс, що відповідає адресі за межами адресного простору масиву.

   Об'єднавши перевантаження оператора з узагальненим класом, можна створити узагальнений тип безпечного масиву, який потім буде використано для створення безпечних масивів, призначених для зберігання даних будь-якого типу. Такий тип масиву і створюється в наведеному нижче коді програми.

Приклад. Демонстрація механізму створення та використання узагальненого класу для організації безпечного масиву

const int size = 10;

template <class aType> class arrClass        // Оголошення класового типу

{           aType aMas[size];

      public:

             arrClass() { for(int i=0; i<size; i++) aMas[i] = i; }

             arrClass &operator[](int i);

};

     // Забезпечення контролю меж для класу aType.

template <class aType> aType &arrClass<aType>::operator[](int i)

{   if(i<0 || i> size-1)

       {   cout << "Значення індексу " << i << " за межами масиву" << endl;

            exit(1);

        }

     return aMas[i];

}

void main()

{    arrClass<int> ObjI;            // Масив int-значень

     arrClass<double> ObjD;        // Масив double-значень

     cout << "Масив int-значень: ";

     for(int i=0; i<size; i++) ObjI[i] = i;

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

     cout << endl;

     cout << "Масив double-значень: ";

     for(int i=0; i<size; i++) ObjD[i] = i/3.0;

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

     cout << endl;

     ObjI[12] = 100;           // Помилка, спроба вийти за межі масиву!

}

   Внаслідок виконання ця програма відображає на екрані такі результати:

Масив int-значень: 0 1 2 3 4 5 6 7 8 9

Масив double-значень: 0 0.333333 0.666667 1 1.333333 1.666667 2 2.333333 2.666667 3

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

  1.   Використання в узагальнених класах аргументів, що не є узагальненими типами

    У template-специфікації для узагальненого класу можна також задавати аргументи, що не є узагальненими типами. Це означає, що в шаблонній специфікації можна вказувати те, що зазвичай приймається як стандартний аргумент, наприклад, аргумент типу int або аргумента-показника. Синтаксис (він практично такий самий, як під час задавання звичайних параметрів функції) містить визначення типу і імені аргумента. Ось, наприклад, як можна по-іншому реалізувати клас безпечного масиву.

Приклад. Демонстрація механізму використання в узагальнених класах аргументів, що не є

                 узагальненими типами

   // Тут елемент int size – це аргумент, що не є узагальненими типом.

template <class aType, int size> class arrClass

{           aType aMas[size];              // В аргументі size передається розмір масиву.__

      public:

            arrClass() { for(int i=0; i<size; i++) aMas[i] = i; }

            arrClass &operator[](int i);

};

   // Забезпечення контролю меж для класу aType.

template <class aType, int size> aType & arrClass<aType, size>::operator[](int i)

{   if(i<0 || i> size-1)

       {   cout << "Значення індексу " << i << " за межами масиву" << endl;

             exit(1);

        }

    return aMas[i];

}

void main()

{    arrClass<int, 10> ObjI;            // 10-елементний масив цілих чисел

     arrClass<double, 15> ObjD;         // 15-елементний масив double-значень

     cout << "Масив цілих чисел: ";

     for(int i=0; i<10; i++) ObjI[i] = i;

     for(int i=0; i<10; i++) cout << ObjI[i] << " ";

     cout << endl;

     cout << "Масив double-значень: ";

     for(int i=0; i<15; i++) ObjD[i] = i/3.0;

     for(int i=0; i<15; i++) cout << ObjD[i] << " ";

     cout << endl;

     ObjI[12] = 100;         // Помилка тривалості виконання!

}

   Внаслідок виконання ця програма відображає на екрані такі результати:

Масив int-значень: 0 1 2 3 4 5 6 7 8 9

Масив double-значень: 0 0.333333 0.666667 1 1.333333 1.666667 2 2.333333 2.666667 3 3.333333

3.666667 4 4.333333 4.666667

   Розглянемо уважно template-специфікацію для класу aType. Зверніть увагу на те, що аргумент size оголошений з вказанням типу int. Цей параметр потім використовують в тілі класу aType для оголошення розміру масиву aMas. Хоча у початковому коді програми член size має вигляд "змінної", його значення відоме вже при компілюванні. Тому його можна успішно використовувати для встановлення розміру масиву. Окрім цього, значення "змінної" size використовують для контролю виходу за межі масиву в операторній функції operator[](). Зверніть також увагу на те, як у функції main() створюється масив цілих чисел і масив значень з плинною крапкою. При цьому розмір кожного з них визначається другим параметром template-специфікації.

   На тип параметрів, які не представляють типи, накладаються обмеження. У цьому випадку дозволено використовувати тільки цілочисельні типи, показники і посилання. Інші типи (наприклад, float) не допускаються. Аргументи, які передаються параметру, що не є узагальненими типом, повинні містити або цілочисельну константу, або показник, або посилання на глобальну функцію чи об'єкт. Таким чином, ці "нетипові" параметри необхідно розглядати як константи, оскільки їхні значення не можуть бути змінені. Наприклад, у тілі функції operator[]() така настанова недопустима:

size = 10;       // Помилка

   Оскільки параметри-"не типи" обробляються як константи, то їх можна використовувати для встановлення розміру масиву, що істотно полегшує роботу програмісту.

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

   

Тема: Використання в шаблонних класах аргументів за замовчуванням

   Шаблонний клас може за замовчуванням визначати аргумент, який відповідає узагальненому типу. Наприклад, внаслідок оголошення такої template-специфікації

template <class aType=int> class myClass { // ...

використовуватиметься тип int, якщо при створенні об'єкта класу myClass відсутнє задавання будь-якого типу.

   Для аргументів, які не представляють тип в template-специфікації, також дозволяється задавати значення за замовчуванням. Вони використовуються у випадку, якщо під час реалізації класу значення для такого аргументу безпосередньо не вказане. Аргументи за замовчуванням для "нетипових" параметрів задаються за допомогою синтаксису, аналогічного використовуваному під час задавання аргументів за замовчуванням для параметрів функцій.

    Розглянемо ще одну версію класу безпечного масиву, у якому використовуються аргументи за замовчуванням як для типу даних, так і для розміру масиву.

Приклад. Демонстрація механізму використання шаблонних аргументів за 

                  замовчуванням

     // Тут параметр aType за замовчуванням приймає тип int, a параметр

     // size за замовчуванням встановлюється таким, що дорівнює 10.

template <class aType=int, int size=10> class arrClass

{   aType aMas[size];         // Через параметр size передається розмір масиву.

public:

     arrClass() { for(int i=0; i<size; i++) aMas[i] = i; }

     arrClass &operator[](int i);

};

     // Забезпечення контролю меж для класу aType.

template <class aType, int size> aType &arrClass<aType, size>::operator[](int i)

{    if(i<0 || i> size-1)

       {   cout << "Значення індексу " << i << " за межами масиву" << endl;

            exit(1);

        }

return aMas[i];

}

void main()

{    arrClass<int, 100> intMas; // 100-елементний масив цілих чисел

     arrClass<double> doubleMas; // 10-елементний масив double-значень

             // (розмір масиву встановлено за замовчуванням)

      arrClass<> defMas; // 10-елементний масив int-значень

       // (розмір і тип int встановлені за замовчуванням)

      cout << "Масив цілих чисел: ";

      for(int i=0; i<100; i++) intMas[i] = i;

      for(int i=0; i<100; i++) cout << intMas[i] << " ";

      cout << endl;

      cout << "Масив double-значень: ";

      for(int i=0; i<10; i++) doubleMas[i] = i/3.0;

      for(int i=0; i<10; i++) cout << doubleMas[i] << " ";

      cout << endl;

      cout << "Масив за замовчуванням: ";

      for(int i=0; i<10; i++) defMas[i] = i;

      for(int i=0; i<10; i++) cout << defMas[i] << " ";

      cout << endl;

}

   Внаслідок виконання ця програма відображає на екрані такі результати:

Масив int-значень: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ... 99

Масив double-значень: 0 0.333333 0.666667 1 1.333333 1.666667 2 2.333333 2.666667 3

Масив за замовчуванням: 0 1 2 3 4 5 6 7 8 9

   Зверніть особливу увагу на цей рядок:

template <class aType=int, int size=10> class arrClass {

    У цьому записі параметр aType за замовчуванням замінюється типом int, а параметр size за замовчуванням встановлюється таким, що дорівнює числу 10. Як показано у наведеному вище коді програми, об'єкти класу aType можна створити трьома способами:

● шляхом безпосереднього задавання як типу, так і розміру масиву;

● задавши безпосередньо тільки тип масиву, при цьому його розмір за замовчуванням встановлюється таким, що дорівнює 10 елементам;

● взагалі без задавання типу і розміру масиву, при цьому він за замовчуванням зберігатиме     елементи типу int, а його розмір за замовчуванням встановлюється таким, що дорівнює 10.

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

Тема: Явні спеціалізації класів

    Подібно до шаблонних функцій можна створювати і спеціалізації узагальнених класів. Для цього використовується конструкція template<>, яка працює за аналогією із безпосередньо заданими спеціалізаціями функцій. Розглянемо такий приклад.

Приклад. Демонстрація безпосередньо заданої спеціалізації класів

template <class tType> class myClass

{           tType x;

     public:

            myClass(tType ObjA)

                    { cout << "У тілі узагальненого класу myClass" << endl; x = ObjA; }

            tType getX() { return x; }

};

  // Безпосередня спеціалізація для типу int.

template <> class myClass<int>

{           int x;

      public:

            myClass(int a) { cout << "У тілі спеціалізації myClass<int>" << endl; x = a * a; }

            int getX() { return x; }

};

void main()

{     myClass<double> ObjD(10.1);

      myClass<int> ObjI(5);

      cout << "int: " << ObjI.getX() << endl;

}

 

  Внаслідок виконання ця програма відображає на екрані такі результати:

У тілі узагальненого класу myClass.

double: 10.1

У тілі спеціалізації myClass<int>.

int: 25

    У цьому коді програми зверніть особливу увагу на такий рядок:

template <> class myClass<int> {

   Він повідомляє компіляторові про те, що створюється безпосередня int-спеціалізація даних класу myClass. Аналогічний синтаксис використовується і для будь-якого іншого типу спеціалізації класу.

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


 

А также другие работы, которые могут Вас заинтересовать

40196. Особенности коммерческих организаций, осуществляющих финансово-кредитные операции. Особенности коммерческих организаций, занимающихся финансовым посредничеством 55 KB
  Особенности коммерческих организаций осуществляющих финансовокредитные операции. Особенности коммерческих организаций занимающихся финансовым посредничеством Кредитная сфера охватывает часть денежных отношений характеризующуюся самостоятельным движением денег когда деньги меняют владельца Д Д. На этой основе у кредитных организаций возникают следующие группы финансовых отношений: денежные отношения характерные для коммерческих организаций: отношения с потребителями банковских услуг что является основой формирования финансовых...
40197. Особенности финансов некоммерческих организаций 22 KB
  Некоммерческие П. Некоммерческие партнерства 7. Автономные некоммерческие организации 8.
40198. Организация управления финансами корпораций. Организационная структура финансовой службы 62.5 KB
  СТРУКТУРА ФИНАНСОВОЙ СЛУЖБЫ ПРЕДПРИЯТИЯ Общее правило формирования коллектива сотрудников финансовой службы предприятия – высокая квалификация и экономически обоснованная минимизация штата. Поэтому структура финансовой службы предприятия обычно формируется постепенно по мере развития предприятия освоения современных технологий и внедрения современных инструментов финансового менеджмента централизации финансово – экономического управления предприятием и формирования на предприятии высокой корпоративной культуры. Структура финансовой службы...
40199. Понятие и сущность финансовых ресурсов. Охарактеризуйте основные подходы к формированию понятия финансовые ресурсы 41.5 KB
  Прибыль – это часть чистого дохода созданного в процессе производства и реализованного в сфере обращения которую непосредственно получает предприятие. Прибыль убыток отчетного периода складывается из: прибыли от реализации товаров; прибыли от реализации имущества и прочих активов; прибыли от финансовых операций; внереализационной прибыли. Прибыль от реализации товаров представляет собой разность выручки от реализации за вычетом НДС акцизов экспортных пошлин налога на реализацию ГСМ процентных надбавок к розничным ценам на...
40200. Подходы к трактовке определения капитала. Учетно-аналитический подход к трактовке понятия капитала 32.5 KB
  Подходы к трактовке определения капитала. Учетноаналитический подход к трактовке понятия капитала. Капитал – это стоимость которая имеет рыночную цену и спрос это реализуемая стоимость. Капитал имеет три формы движения: предпринимательскую прямые капиталовложения в производство и портфельные инвестиции т.
40201. Экономическая сущность капитала. Принципы его формирования 35.5 KB
  Экономическая сущность капитала. Рассматривая экономическую сущность капитала предприятия отметим следующие его главные черты: основной фактор производство; источник формирования благосостояния собственников; капитал предприятия является измерителем его рыночной стоимости. Вместе с тем объем используемого предприятием собственного капитала характеризует одновременно и потенциал привлечения им заемных средств; Динамика капитала предприятия является важнейшим показателем уровня эффективности его хозяйственной деятельности то есть за...
40202. Собственный капитал и его основные элементы. Расчет чистых активов 36.5 KB
  Расчет чистых активов Собственный капитал – общая стоимость средств пря принадлежащих ему на праве собственности. Для определения стоимости собственного капитала используется показатель стоимость чистых активов пря Ча=АрПр где где р принимаемые к расчету Аактивы Ппассивы Ча пря рассчитывается ежеквартально. Под стоимостью чистых активов акционерного общества понимается величина определяемая путем вычитания из суммы активов акционерного общества принимаемых к расчету суммы его пассивов принимаемых к расчету. Порядок расчета...
40203. Заемный капитал и его источники формирования 26 KB
  Краткосрочные Краткосрочные кредиты банков Краткосрочные займы Кредиторская задолженность По источникам привлечения: 1. Из внутренних источников кредиторская задолженность Цели привлечения: 1 – инвестиционные цели создание запасов; 2 – обеспечение непрерывности производственного процесса потребности пря в ресурсах. Кредиторская задолженность Задолженность поставщикам подрядчикам за выполненные работы поступившие ценности Начисленная но не выплаченная з...
40204. Эмиссия облигаций как источник финансирования 28 KB
  Эмиссия облигаций как источник финансирования. Эмиссия облигаций. Одним из источников финансирования инвестиционной деятельности может быть эмиссия облигаций направленная на привлечение временно свободных денежных средств населения и коммерческих структур.Привлекательность облигаций для потенциальных инвесторов во многом определяется условиями их размещения.