68958

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

Лекция

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

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

Украинкский

2014-09-28

62 KB

1 чел.

Лекція № 20

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

План

  1.  Узагальнені класи
  2.  Приклад використання двох узагальнених типів даних
  3.  Застосування аргументів за умовчанням в шаблонних класах
  4.  Явні спеціалізації класів

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

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

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

Оголошення узагальненого класу має наступний вигляд.

template <class Ттип> class имя_класса {

}

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

Конкретний екземпляр узагальненого класу створюється за допомогою наступної синтаксичної конструкції.

имя_класса <тип> имя_объекта;

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

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

// Демонстрація узагальненого стека.

#include <iostream>

using namespace std;

const int SIZE = 10;

// Створюємо узагальнений клас stack.

template <class StackType> class stack

{

StackType stck[SIZE]; // Містить елементи стека.

int tos; // Індекс вершини стека.

public:

stack() { tos = 0;} // Ініціалізував стек.

void push(StackType ob); // Заштовхує об'єкт в стек.

StackType pop(); // Виштовхує об'єкт із стека.

};

// Заштовхуємо об'єкт в стек.

template <class StackType> void stack<StackType>::push(StackType ob)

{

if(tos==SIZE){

cout << "Стек повний.\ п";

return;

}

stck[tos]= ob;

tos++;

}

// Виштовхуємо об'єкт із стека.

template <Class StackType> StackType stack<StackType>::pop()

{

if(tos==0){

cout << "Стек порожній..\n";

return 0; // Якщо стек порожній, повертається константа null.

}

tos--;

return stck[tos];

}

int main()

{

// Демонстрація стека символів.

stack<char> s1, s2; // Створюємо два стеки символів.

int i ;

s1.push ('a'); s2.push('x') s1.push('b' ); s2.push('y');

s1.push('c'); s2.push(' z' );

 for(i=0; i<3; i++) cout << "Виштовхуємо s1: “ « s1.pop() « '\n';

for(i=0; i<3; i++) cout << "Виштовхуємо s2: “ « s2.pop() « '\n';

// Демонстрація стека дійсних чисел

stack<double> dsl, ds2; // Створюємо два стеки дійсних чисел.

dsl.push(1.1); ds2.push(2.2); dsl.push(3.3); ds2.push(4.4);

dsl.push(5.5); ds2.push(6.6);

for(i=0; i<3; i++) cout « "Виштовхуємо dsl: " « dsl.pop() « "\n";

for(i=0; i<3; i++) cout « "Виштовхуємо ds2: " « ds2.pop() « "\n";

return 0;

}

Як видимий, оголошення узагальненого класу мало відрізняється від оголошення узагальненій функції. Фактичний тип даних, що розміщується в стеку, в оголошенні класу замінюється узагальненим параметром і уточнюється лише при створенні конкретного об'єкту. При оголошенні конкретного об'єкту класу stack компілятор автоматично генерує всі функції і змінні, необхідні для обробки фактичних даних. У попередньому прикладі оголошуються по два стеки різних типів — цілих чисел і дійсних чисел. Зверніть особливу увагу на наступні оголошення.

stack<char> s1, s2; // Створюємо два стеки символів.

stack<double> dsl, ds2; // Створюємо два стеки дійсних чисел.

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

stack<char *> chrptrQ;

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

struct addr {

char name[40];

char street[40];

char city[30];

char state[3];

char zip[12];

};

В цьому випадку клас stack породжує стек, в якому зберігаються об'єкти класу addr Для цього використовується наступне оголошення.

stack<addr> obj;

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

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

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

 

/* Приклад класу, що використовує два узагальнені типи. */

#include <iostream>

using namespace std;

template <class Typel, class Type2> class myclass

{

Typel i;

Type2 j;

public:

myclass(Typel а, Type2 b) { i = а; j = b; }

void show() { cout « i « ' ' « j « '\n'; }

};

int main()

{

myclass<int, double> ob1(10, 0.23);

myclass<char, char *> ob2('X', "Шаблони — могутній механізм.");

obl.show(); // Виводиться ціле і дійсне число.

ob2.show(); // Виводиться символ і покажчик на символ.

return 0;

}

Ця програма виводить наступні результати.

10 0.23

X Шаблони — могутній механізм.

У програмі оголошуються об'єкти двох типів. Об'єкт obl використовує цілі і дійсні числа. Об'єкт ob2 використовує символ і покажчик на символ. У обох випадках при створенні об'єктів компілятор автоматично генерує відповідні дані і функції.

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

Шаблонний клас може мати аргумент узагальненого типу, значення якого задане за умовчанням. Наприклад, такий.

template <Class X=int> class myclass { //...

Якщо при конкретизації об'єкту типу myclass не буде вказаний жоден тип, використовується тип int.

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

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

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

#include <iostream>

#include <cstdlib>

using namespace std;

// Параметр типу Атуре за умовчанням рівний int,

// а змінна size за умовчанням рівна 10.

template <class AType=int, int size=10> class atype {

AType а[size]; // Розмір масиву передається аргументом size.

public:

atype() {

register int i;

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

}

AType &operator[](int i); };

// Перевірка діапазону для об'єкту atype.

template<class AType,int size> AType &atype<AType,size>::operator[](int i){

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

cout « "\nЗначение індексу ";

cout « i « " виходить за межі допустимого даипаэона.\n";

exit(l);

}

return а[i];

}

int main()

{

atype<int, 100> intarray; // Цілочисельний масив з 100 елементів.

atype<double> doublearray; // Масив дійсних чисел 

//розмір заданий за умовчанням.

atypeo defarray; //За умовчанням оголошується цілочисельний

// масив, що складається з 10 елементів.

int i ;

cout « "Цілочисельний масив: ";

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

for(i=0; i<100; i++) cout « intarray[i]« " ";

cout « '\n' ;

cout « "Масив дійсних чисел: ";

for(i=0; i<10; i++) doublearray[i]= (double) i/3;

for(i=0; i<10; i++) cout « doublearray[i] << " ";

cout « ' \n' ;

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

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

for(i=0; i<10; i++) cout « defarray[i]« " ";

cout << '\n';

return 0;

}

Зверніть увагу на рядок

template <class AType=int, int size=10> class atype {

Тут тип АTуре за умовчанням є типом int, а змінна size рівна 10. Як демонструє програма, об'єкти класу atype можна створити трьома способами.

• Явно задаючи тип і розмір масиву.

• Явно задаючи тип масиву, використовуючи розмір за умовчанням.

• Використовуючи тип і розмір масиву, встановлені за умовчанням.

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

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

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

// Демонстрація спеціалізації класу.

#include <iostream>

using namespace std;

template <class T> class myclass {

T x;

public:

myclass(T а){

cout « "Усередині узагальненого класу myclass\n";

x = а;

}

T getx() { return x; }

};

// Явна спеціалізація для типу int.

template<> class myclass<int> {

int x;

public:

myclass(int а){

cout « "Усередині спеціалізації myclass<int>\n";

x = а * а;

}

int getx() { return x; }

};

int main() {

myclass<double> d(10.1);

cout « "double: " « d.getx() « "\n\n";

myclass<int> i(5);

cout « "int: " « i.getx() « "\n";

return 0;

}

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

Усередині узагальненого класу myclass

double: 10.1

Зверніть увагу на наступний рядок програми.

template <> class myclass<int> {

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

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