Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Namespace CounterNameSpace 7 страница




{

if (rloc == sloc){ cout << "Черга порожня.\n"; return 0;}

rloc++;

return qpMas[rloc];

}

 

int main ()

{

// Створюємо дві черги для цілих чисел.

chergaClass< int > a_ob, b_ob;

 

a_ob.putQp(10);

b_ob.putQp(19);

a_ob.putQp(20);

b_ob.putQp(1);

 

cout << a_ob.getQp() << " ";

cout << a_ob.getQp() << " ";

cout << b_ob.getQp() << " ";

cout << b_ob.getQp() << "\n";

 

// Створюємо дві черги для double -значень

chergaClass< double > c_ob, d_ob;

c_ob.putQp(10.12);

d_ob.putQp(19.99);

c_ob.putQp(-20.0);

d_ob.putQp(0.986);

 

cout << c_ob.getQp() << " ";

cout << c_ob.getQp() << " ";

cout << d_ob.getQp() << " ";

cout << d_ob.getQp() << "\n";

 

getch (); return 0;

}

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

10 20 19 1

10.12 -20 19.99 0.986

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

chergaClass< int > a_ob, b_ob;

chergaClass< double > c_ob, d_ob;

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

chergaClass< char *> chrptrQ;

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

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

char name[40];

char street[40];

char city[30];

char state[3];

char zip[12];

};

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

chergaClass<addrStruct> obj;

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

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

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

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

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

 

template < class aType, class bType> class myClass{

aType izm;

bType jzm;

public:

myClass(aType a_ob, bType b_ob) { izm = a_ob; jzm = b_ob;}

void showType()

{ cout << "izm= " << izm << "; jzm= " << jzm << "\n";}

};

 

int main ()

{

myClass< int, double > A_ob(10, 0.23);

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

 

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

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

 

getch (); return 0;

}

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

izm= 10; jzm= 0.23

izm= x; jzm= Це тест.

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

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

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

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

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

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

 

const int size = 10;

 

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

aType aMas[size];

public:

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

aType & operator [](int i);

};

 

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

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

{

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

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

exit (1);

}

 

return aMas[i];

}

 

int main ()

{

atype< int > I_ob; // Масив int -значень

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

int i;

 

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

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

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

cout << "\n";

 

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

for (i=0; i<size; i++) D_ob[i] = (double) i/3;

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

cout << "\n";

 

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

 

getch (); return 0;

}

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

Масив 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 -значень. Було б непогано, якби Ви спробували створити масиви інших типів. Як доводить цей приклад, одна з великих переваг узагальнених класів полягає у тому, що вони дають змогу тільки один раз написати програмний код, відлагодити його, а потім застосовувати його до даних будь-якого типу, не переписуючи його для кожного конкретного застосування.

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

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

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

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

 

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

template < class aType, int size> class atype {

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

public:

atype() { register int i; for (i=0; i<size; i++) aMas[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 << "Значення індексу " << i << " за межами масиву.\n";

exit (1);

}

return aMas[i];

}

 

int main ()

{

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

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

int i;

 

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

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

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

cout << "\n";

 

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

for (i=0; i<15; i++) D_ob[i] = (double) i/3;

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

cout << "\n";

 

I_ob[12] = 100; // помилка часу виконання!

 

getch (); return 0;

}

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

Масив 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; // помилка

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

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

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

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

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

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

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

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

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

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

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

 

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

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

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

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

public:

atype() { register int i; for (i=0; i<size; i++) aMas[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 << "Значення індексу " << i << " за межами масиву.\n";

exit (1);

}

return aMas[i];

}

 

int main ()

{

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

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

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

atype<> defMas; // 10-елементний масив int -значень (розмір

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

int i;

 

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

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

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

cout << "\n";

 

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

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

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

cout << "\n";

 

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

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

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

cout << "\n";

 

getch (); return 0;

}

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

Масив 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 atype {

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

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

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

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

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

17.2.6. Безпосередньо задані спеціалізації класів

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

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

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

 

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

tType x;

public:

myClass(tType a_ob){

cout << "У тілі узагальненого класу myClass.\n";

x = a_ob;

}

tType getXzm() { return x;}

};

 

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

template <> class myClass< int > {

int x;

public:

myClass(int a) {

cout << "У тілі спеціалізації myClass< int >.\n";

x = a * a;

}

int getXzm() { return x;}

};

 

int main ()

{

myClass< double > D_ob(10.1);

cout << " double: " << D_ob.getXzm() << "\n\n";

 

myClass< int > I_ob(5);

cout << " int: " << I_ob.getXzm() << "\n";

 

getch (); return 0;

}

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

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

double: 10.1

 

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

int: 25

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

template <> class myClass< int > {

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

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


Розділ 18. Оброблення виняткових ситуацій

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

У цьому розділі ми також повертаємося до С++-операторів динамічного розподілу пам'яті: new і delete. Як пояснювалося вище у цьому навчальному посібнику, якщо оператор new не може виділити необхідну пам'ять, він генерує винятки. І тут ми дізнаємося про те, як саме обробляються такі винятки. Окрім того, Ви навчитеся перевантажувати оператори new і delete, що дасть змогу Вам визначати власні схеми виділення пам'яті.

18.1. Основи оброблення виняткових ситуацій

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

Керування С++-механізмом оброблення винятків тримається на трьох ключових словах: try, catch і throw. Вони утворюють взаємозв'язану підсистему, у якій використання одного з них припускає застосування іншого. Спершу буде корисно отримати загальне представлення про роль, яку вони відіграють під час оброблення виняткових ситуацій.

18.1.1. Системні засоби оброблення винятків

Якщо стисло, то їх робота полягає в наступному. Програмні настанови, які Ви вважаєте за потрібне проконтролювати на предмет винятків, поміщаються в try -блок. Якщо виняток (тобто помилка) таки виникає у цьому блоці, то він дає знати про себе шляхом викидання певного роду інформації (за допомогою ключового слова throw). Цей викинутий виняток можна перехопити програмним шляхом за допомогою catch -блоку і оброблений відповідним чином. А зараз сказане постараємося розглянути грунтовніше.

Настанова throw генерує виняток, який перехоплюється catch -настановою.

Отже, програмний код, у якому можливе виникнення виняткових ситуацій, повинен виконуватися у межах try -блоку. Будь-яка функція, що викликається з цього try -блоку, також піддається контролю. Винятки, які можуть бути викинуті контрольованим кодом, перехоплюються catch -настановою, безпосередньо наступною за try -блоком, у якому фіксуються ці "викиди" винятків. Загальний формат try - і catch -блоків має такий вигляд:

try {

// try-блок (блок коду програми, що підлягає перевірці

// на наявність помилок)

}

catch (aType arg) {

// catch-блок (обробник винятків типу aType)

}

catch (bType arg) {

// catch-блок (обробник винятків типу bType)

}

catch (cType arg) {

// catch-блок (обробник винятків типу cType)

}

//....

catch (nType arg) {

// catch-блок (обробник винятків типу nType)

}

Блок try повинен містити програмний код, який, на Вашу думку, повинен перевірятися на предмет виникнення помилок. Цей блок може містити тільки декілька настанов деякої функції або охоплювати весь код функції main () (у цьому випадку, по суті, "під ковпаком" системи оброблення винятків знаходитиметься вся програма).

Після "викиду" виняток перехоплюється відповідною настановою catch, яка виконує його оброблення. З одним try -блоком може бути пов'язана не одна, а декілька catch -настанов. Яка саме з них буде виконана, визначається типом винятку. Іншими словами, буде виконана та catch -настанова, тип винятку якої (тобто тип даних, що задається в catch -настанові) збігається з типом згенерованого винятку (а всі інші будуть проігноровані). Після перехоплення винятку параметр arg прийме його значення. Таким шляхом можуть перехоплюватися дані будь-якого типу, в т.ч. об'єкти класів, що були створені програмістом.




Поделиться с друзьями:


Дата добавления: 2014-11-29; Просмотров: 420; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.141 сек.