Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МЕТОД ЛАБ ООР++.doc
Скачиваний:
2
Добавлен:
01.07.2025
Размер:
404.99 Кб
Скачать

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

1. У чому полягає необхідність відмінності об'єктів-екземплярів і об'єктів-класів.

2. Що таке ідентифікатор об'єкту.

3. В чому різниця між тимчасовими та постійними об'єктами

4. Що таке тимчасовий та постійний зв'язок.

5. Хороша об'єктна модель відрізняється тим, що більшість атрибутів в ній закрито, а більшість зв'язківвідкрито. Чому?

6. В чому різниця між композицією та агрегацією.

7. Що таке клас- сутність.

8. В чому полягає різниця між дією та діяльністю на діаграмі станів.

9. В чому різниця між множинною класифікацією та множинним наслідуванням.

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

11. У чому переваги використання абстрактних класів

Лабораторна робота № 5 Розробка шаблонів класів і функцій

Мета: Вивчення поняття шаблонів класів і функцій С++ та принципів роботи з ними

Основні теоретичні відомості

Поняття шаблону

Шаблони в С++ є схематичним описом побудови функцій або класів. Ці описи за своє структурою є близькими до функцій чи класів і називються родовими фукціями чи родовими класами, або просто шаблонами. Визначаються шаблони зарезервованим словом template.

За своєю суттю шаблони є функціями чи класами загального призначення (специфікаціями) і не пов'язані з конкретним типом даних. Типи загального призначення, якими оперують шаблони, називаються шаблонними (родовими) типами, а їх сукупність параметрами шаблона. Параметри шаблону як множина шаблонних типів може містити також преозначені і вбудовані типи С++.

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

Шаблонний тип Т є невизначеним узагальненим типом. По мірі використання шаблонів компілятор автоматично замінить тип Т іменем реального типу. Як правило, для імені шаблонного типу використовують індентифікатори Т чи Type. Проте це не обов'язково: ім'я можна декларувати будь-яким допустимим в С++ індентифікатором. Шаблонний тип Т можна повноцінно використовувати в тілі шаблону, як преозначений, але це не є строгою ви­могою.

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

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

Шаблон функції (ШФ або родова функція) за своєю структурою є зви­­чайною функцією, яка оперує з абстрактним (шаблонним) типом Т, і синтаксично відрізняється від неї лише декларацією заголовка. Подібно до функцій ого­лошення протипу ШФ є обов'язковим.

Загальний синтаксис оголошення шаблону фунції має вигляд

template <class T[,інші_шабл_типи]> тип_повер

Ім'я_Функ(парам) {

//тіло функціїї

}

На прикладі програми обміну значень двох змінних, приведемо ого­лошення ШФ і його використання (приклад 1)

#include <iostream>

// прототипи ШБ

template <class Type> void Swap (Type&,Type&);

template <class Type2,class Type1 = char*> void Out

(Type1 ,Type2 ,Type2,char c = ' ');

main(){

short a = 2, b = 4;

// використання екземплярів ШБ

Out("Вхідні значення:", a, b);

Swap(a, b); Out("Вихідні значення:", a, b);

double c = 2000, d = 4000;

Out("Вхідні значення:", c, d);

Swap(c, d); Out("Вихідні значення:", c, d);

}

// оголошення ШБ

template <class Type> void Swap (Type &a,Type &b){

Type temp;

temp = a;

a = b;

b = temp;

}

template <class Type2,class Type1 = char*> void Out

(Type1 mes, Type2 a, Type2 b, char c = ' '){

cout << mes << c << a << c << b << "\n";

}

У цій програмі на шаблон Swap вирішує основне завдання, дає можливість зреалізувати обмін значеннь для двох змінних довільного типу, для якого визначено оператор присвоєння. Шаблон Out вирішує завдання виводу змінних наблонного типу Type2 на екран з повідомленням типу Type1, який по замовчуванні є ініціалізований типом char*. Окрім параметрів шаблонних типів Type1 i Type2, шаблон Out приймає один параметр вбудованого типу char з аргументом по замовчуванні ‘ ‘.

Прикладами використання шаблонів є виклики функцій Swap i Out з аргументами різних типів в тілі функціїї main().

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

Шаблони класів і контейнери

За своєю структурою шаблон класу (ШК або родовий клас) є звичайним класом, а тому будується за методологією оголошення класів. Основ­ною відмінністю є наявність зарезервованого слова template в оголошені шаблона класу.

Загальний синтаксис оголошення ШК має вигляд

template <class T[,інші_шабл_типи]> class Ім'я_Класу {

// закриті по замовчуванні члени

public:

// відкриті члени

private:

// закриті члени

potected:

// захищені члени

};

Екземпляр ШК називається шаблонним класом або контейнером. Реальні типи для заміщення шаблонних типів передаються як параметри при створенні об'єкта. Синтаксис створення контейнера і об'єкта виглядає так

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

Після наведених декларацій говорять про оголошення шаблона Ім'я_Класу , побудову шаблонного класу (контейнера) Ім'я_Класу <тип> та створення об'єкта об'єкт.

Наведемо приклад оголошення шаблону класу і його використання (приклад 2)

#include <iostream>

template <class data_t> class list {

data_t data;

list *next;

static int count;

public:

list (data_t d);

void add(list *node) {node->next = this; next = 0;}

list *getnext() {return next;}

data_t getdata() {return data;}

};

template <class data_t> list<data_t>::list(data_t d){

count++;

cout<<count<<"- об\'єкт добавлено"<<"\n";

data = d;

next = 0;

}

int list<char>::count=0; // визначення статичного члена

// але вже для конкретного класу

int main() {

list<char> start('a');

list<char> *p, *last;

int i;

last = &start; // створення списку

for(i=1; i<26; i++) {

p = new list<char> ('a' + i);

p->add(last);

last = p;

}

p = &start; // вивід списку

while(p) {

cout << p->getdata();

p = p->getnext();

}

return 0;

}

Функції-члени ШК автоматично стають ШФ. Їх реалізація може бути як в межах класу (шаблон getnext() чи getdata()), так і за його межами (шаблон-конструктор list ()). Крім цього функції члени ШК млжуть бути вбудованими, перевантажуватись, приймати аргументи по замовчуванні і приймати змінну кількість параметрів.

Загальний синтаксис реалізації функції-члена ШК за межами тіла має вигляд

template <class T[,інші_шабл_типи]> тип_повер

Ім'я_Класу<T[,інші_шабл_типи]>::Ім'я_Функ(парам) {

// тіло функціїї

}

Так реалізований конструктор list() з прикладу 2.

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

Наслідування у шаблонах

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

Як приклад побудови дерева родового наслідування, приведемо ієрархію для впорядкованого (в даному випадку за зростанням) зберігання пар значень (приклад 3)

#include <iostream>

// оголошення батьківського класу

template <class Type> class Data {

Type a, b;

public:

Data(){In(0, 0);} // конструктор

virtual void Out() = 0; // вивід

In(Type i, Type j){a = i; b = j;} // ініціалізація

Type GetA(){return a;}

Type GetB(){return b;}

};

// оголошення похідного класу

template <class Type1> class MinMaxData: public

Data <Type1> {

public:

MinMaxData(Type1, Type1); // конструктор

MinMax(Type1, Type1); // сортування

void Out(); // вивід

};

template <class Type1> void MinMaxData <Type1>::Out() {

cout << "Min: " << Data<Type1>::GetA() << " Max: "

<< Data<Type1>::GetB() << "\n";

}

// конструктор похідного ШК з передаванням параметрів у

// батьківський ШК

template <class Type1>

MinMaxData<Type1>::MinMaxData(Type1 i,Type1 j):

Data<Type1>(){

MinMax(i, j);

}

template <class Type1> MinMaxData<Type1>::MinMax(Type1

i, Type1 j){

if (i < j) In(i, j); else In(j, i);

}

main() {

MinMaxData<int>

MinMaxData1(10, 6); MinMaxData1.Out();

MinMaxData<double>

MinMaxData2(1000, 6000); MinMaxData2.Out();

}

Шаблони і дружність

Шаблони можуть бути дружніми до класів чи до інших шаблоеів класів. Це дає можливість без додаткових зусиль розширити властивість дружності на цілі сім’ї. класів.

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

Як приклад, програму з прикл.3 перепишемо з використання властивості дружності серед ШК (приклад 4).

#include <iostream>

// попереднє оголошення дружнього класу

template <class Type1> class MinMaxData;

template <class Type> class Data {

Type a,b;

public:

Data(){In(0,0);}

In(Type i, Type j){a = i; b = j;}

friend class MinMaxData<Type>; // оголош. дружності

};

template <class Type1> class MinMaxData{

public:

Data<Type1> D;

MinMaxData(Type1, Type1); // конструктор

MinMax(Type1, Type1); // сортування

void Out(); // вивід

};

// доступ до закритих членів ШК Data з дружнього ШК

template <class Type1> void MinMaxData <Type1>::Out() {

cout << "Min: " << D.a << " Max: " << D.b << "\n";

}

// конструктор дружнього ШК

template <class Type1>

MinMaxData<Type1>::MinMaxData(Type1 i,Type1 j){

MinMax(i, j);

}

template <class Type1> MinMaxData<Type1>::MinMax(Type1

i, Type1 j){

if (i < j) D.In(i, j); else D.In(j, i);

}

main() {

MinMaxData<int>

MinMaxData1(10, 6); MinMaxData1.Out();

MinMaxData<double>

MinMaxData2(1000, 6000); MinMaxData2.Out();

}