Шаблони

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

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

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

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

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

 

Приклад:

 

#include <iostream>

 

using namespace std;

 

// оголошення: максимум із двох значень типу Т

template <class T>

const T& Max (const T&, const T&);

 

void main()

{

int i=5, j=10;

float a=1.5, b=1.7;

int k = Max(i, j); // виклик Max(int, int)

float c = Max (a, b); // виклик Max(float, float)

cout<<k<<'t'<<c<<endl;

}

 

// визначення

template <class T>

const T& Max (const T& a, const T& b)

{

return a>b?a:b;

}

 

Шаблони функцій можуть бути перевантажені іншими функціями-шаблонами або звичайними функціями.

 

template <class T> const T& Max (const T&, const T&);

template <class T> const T& Max(const T*, int);

 

Для певних типів шаблони функцій можуть бути перекриті для того, щоб виконувати (або не виконувати) які-небудь дії, які функції-шаблони не виконують (чи виконують).

 

const char* Max (const char* c, const char* d)

{

// виконати що-небудь, специфічне для char*

}

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

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

 

// оголосити клас Stack, що представляє

// собою стек для будь-яких типів

template <class T>

class Stack

{

T* v; // покажчик на деякий тип Т

int size, top;

public:

Stack(int ezis);

~Stack();

void Push(const T&); // помістити Т у стек

T& Pop(); // витягти Т зі стека

T& Top()const;

};

 

// ...

Stack <int> i; // стек для int

Stack <char*> cp; // стек для char*

 

Шаблони класів можуть мати нетипізовані (або тільки нетипізовані) параметри. Значення, зазначені для цих параметрів, повинні бути константними виразами.

 

// передати розмір як параметр шаблона

template <class T, int size>

class Stack

{

T v[size]; // масив елементів типу Т

int top;

public:

Stack():top(-1){}

// ...

};

// ...

 

Stack <int, 20> tiny;

Stack <int, 500> huge;

 

Хоча стеки tiny і huge зберігають тип int, це різні типи, оскільки вони мають різний розмір стека. Це можна проілюструвати тим, що покажчик Stack<int, 20> – це не те ж саме, що покажчик на Stack<int, 500>:

 

Stack<int, 20>* is20p = &tiny; // правильно

Stack<int, 500>* is500p = &tiny; // помилка

 

Шаблонові класи можуть бути породжені як від нешаблонних класів, так і від класів- шаблонів. А також можуть породжувати як нешаблонні класи, так і класи-шаблони.

 

class A {/* … */};

template <class T> class B: public A{/* … */};

template <class T> class C: public B{/* … */};

class D: public C<int> {/* … */};

Для певних типів шаблони класів можуть бути перекриті для того, щоб виконувати (або не виконувати) які-небудь дії, які шаблони класів не виконують (чи виконують).

 

// оголосити свій власний стек для char*

class Stack<char*>

{

char** v; // покажчик на char*

int size, top;

public:

Stack (int ezis);

~Stack();

void Push(const char*);

char* Pop();

char* Top() const;

};

Шаблонові класи можуть також бути класами-структурами або класами-об’єднаннями.

4. Статичні дані-члени

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

 

template <class T>

class C

{

static int i; // звичайне статичне поле

static T t; // параметризоване

// ...

};

template <class T> int C<T>::i; // визначити в області

template <class T> T C<T>::t; // видимості файлу

// ...

C<char> c; // має int C::i і char C::t

C<float> f; // має int C::i і float C::t

5. Шаблони функцій-членів

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

 

template <class T>

void Stack<T>::Push(const T& element)

{

if(top == size - 1)

error("Stack overflow");

else

v[++top] = element;

}

 

Для певних типів шаблони функцій-членів можуть бути перекриті для того, щоб виконувати (або не виконувати) які-небудь дії, які шаблони функцій-членів не виконують (чи виконують).

 

void Stack<char*>::Push(const char* cepr)

{

// виконати щось особливе з char*

}

6. Дружні функції

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

Дружні функції можуть випереджатися специфікацією template. Для типів Т і U функції-шаблони типу U є дружніми функціями кожному класу типу Т.

 

template <class T>

class Person

{

friend void Pet();

template <class T> friend void Spouse(Person&);

template <class U> friend void Coworker(U&);

};

// ...

void Pet() // звичайна функція

{

// ...

}

 

template <class T>

void Spouse(Person& p)

{

// ...

}

 

template <class U>

void Coworker(U& u)

{

// ...

}

 

Тут Pet() – функція, дружня Person <T> для кожного типу Т.

 

 
 


Pet()

Person<int>
Person<char>
Person<float>

 

Для будь-якого типу Т, скажімо, int, Spouse(Person<int>&) – функція, дружня Preson<int>, але не Person<char> або будь-якому іншому типу.

 

Spouse(Person<int>&) Person<int>
Spouse(Person<char>&) Person<char>
Spouse(Person<float>&) Person<float>

 

Coworker(U&) – функція, дружня Person<T>, для будь-якого типу Т і будь-якого типу U.

 

Coworker(int&) Person<int>
Coworker(char&) Person<char>
Coworker(float&) Person<float>

 

 

Дружні функції можуть бути функціями-членами іншого класу.

 

template <class T>

class Person

{

friend void Family::Sibling();

friend void Acquaintance::Casual(Person&);

template <class U> friend void Neighbor<U>::NextDoor(U&);

};

 

Тут Family::Sibling() – функція, дружня Person<T>, для кожного типу Т. Для будь-якого типу Т, скажімо, для int, Acquaintance::Casual(Person<int>&) – функція, дружня Person<int>, але не Person<char> або будь-якому іншому типу.

Neighbor<U>::NextDoor(U&) – функція, дружня Person<T>, для будь-якого типу U.

Дружні функції можуть бути оголошені для всього класу.

 

template <class T>

class Person

{

friend class Family;

friend class Acquaintance;

template <class U> friend class Neighbor;

};

 

Тут для кожного типу Т усі функції-члени класу Family – це функції, дружні Person<T>. Для будь-якого типу Т, скажімо, для int, усі функції класу Acquaintance<int> є друзями Person<int>, але не Person<char> або будь-якого іншого типу.

Для кожного типу Т і кожного типу U всі функції-члени Neighbor<U> є друзями Person<T>.

Дружні функції можуть також бути друзями нешаблонних класів.

 

Приклад:

 

// TDatabase.h

 

#ifndef __TDATABASE_H

#define __TDATABASE_H 1 // запобігання декільком #include

 

template <class T>

class TDatabase

{

T* rp; // покажчик на записи

int num; // кількість записів

public:

TDatabase(int n)

{

rp = new T[num = n];

}

~TDatabase(void)

{

delete[] rp;

}

 

T& GetRecord(int recnum);

};

 

template <class T>

T& TDatabase<T>::GetRecord(int recnum)

{

T* crp = rp; // покажчик на поточний запис = покажчик на записи

if(0 <= recnum && recnum < num)

while(recnum-- > 0)

crp++;

return *crp;

}

 

 

#endif // __TDATABASE_H

 

// CTemplat.cpp

#include <iostream>

#include <cstring>

#include "TDatabase.h"

 

using namespace std;

 

class TRecord

{

private:

char name[41];

public:

TRecord()

{

name[0] = 0;

}

TRecord(const char* s)

{

Assign(s);

}

char* GetName(void)

{

return name;

}

void Assign(const char* s)

{

strncpy(name, s, 40);

}

};

 

void main()

{

int rn; // індекс кількості записів

TDatabase<TRecord> db(3); // база даних із трьох TRecord

TDatabase<TRecord*> dbp(3); // із трьох покажчиків

TDatabase<TRecord> *pdb; // покажчик на базу даних

TDatabase<TRecord*> *ppdb; // покажчик на базу даних покажчиків на

//TRecord

 

cout<<"nnDatabase of 3 TRecordsn";

db.GetRecord(0).Assign("George Washington");

db.GetRecord(1).Assign("John Adams");

db.GetRecord(2).Assign("Thomas Jefferson");

for(rn=0; rn<=2; rn++)

cout<<db.GetRecord(rn).GetName()<<'n';

 

cout<<"nnDatabase of 3 TRecord pointersn";

dbp.GetRecord(0) = new TRecord("George Bush");

dbp.GetRecord(1) = new TRecord("Ronald Reagan");

dbp.GetRecord(2) = new TRecord("Jimmy Carter");

for(rn=0; rn<=2; rn++)

cout<<dbp.GetRecord(rn)->GetName()<<'n';

 

cout<<"nnPointer to Database of 3 TRecordsn";

pdb = new TDatabase<TRecord>(3);

pdb->GetRecord(0).Assign("John Adams");

pdb->GetRecord(1).Assign("Thomas Jefferson");

pdb->GetRecord(2).Assign("Aaron Burr");

for(rn=0; rn<=2; rn++)

cout<<pdb->GetRecord(rn).GetName()<<'n';

 

cout<<"nnpointer to Database of 3 TRecord pointersn";

ppdb = new TDatabase<TRecord*>(3);

ppdb->GetRecord(0) = new TRecord("Dan Quayle");

ppdb->GetRecord(1) = new TRecord("George Bush");

ppdb->GetRecord(2) = new TRecord("Walter Mondale");

for(rn=0; rn<=2; rn++)

cout<<ppdb->GetRecord(rn)->GetName()<<'n';

 

}

 

 


Лекція 17. Виняткові ситуації

1. Виняткові ситуації

Виняткова ситуація – це ні що інше, як умова виняткової ситуації, що вимагає спеціальної обробки.

Для збудження виняткової ситуації оператор посилає (throw) об’єкт, що описує суть виняткової ситуації. Об’єкт може бути літеральним значенням, рядком, об’єктом класу або будь-яким іншим об’єктом.

Для обробки виняткової ситуації деякий оператор перехоплює (catch) умову, надіслану деяким процесом. Оператори, що перехоплюють виняткові ситуації, називаються оброблювачами виняткових ситуацій.

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

2. Використання виняткових ситуацій

Звичайно в класі виняткової ситуації реалізуються функції-члени, які можна викликати у виразах усередині catch.

 

class Overflow

{

public:

void Report()

{

cout<<"Error: overflow"<<endl;

}

};

 

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

 

throw Overflow();

 

В операторі catch можна викликати Report() об’єкта виняткової ситуації для відображення повідомлення про помилку:

 

catch(Overflow overObject)

{

overObject.Report();

}

3. Збудження декількох виняткових ситуацій

У функціях можна збуджувати об’єкти виняткових ситуацій різних типів для підтримки різних умов виняткових ситуацій.

 

int AnyFunction()

{

if(condition1)

throw "Big trouble!n"; // послати рядковий об'єкт

if(condition2)

throw Overflow(); // послати об'єкт класу

return 123; // якщо немає проблем, повернути значення

}

 

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

Збудження виняткової ситуації негайно завершує виконання функції, у якій виконується оператор throw.

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

 

catch(Error e)

{

// обробка виняткової ситуації для класу Error

}

catch(const char* message)

{

// обробка виняткової ситуації для рядків

}

4. Використання блоків try

try

{

cout<<"Here we go!"<<endl;

int x = AnyFunction();

cout<<"x == "<<x<<endl;

}

catch(const char* message)

{

cout<<"Error! "<<message<<endl;

exit(-1); // необов'язково

}

catch(Overflow)

{

cout<<"Overflow!"<<endl;

exit(-2); // необов'язково

}

 

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

5. Оголошення виняткових ситуацій

 

void AnyFunction()throw(Error);

 

Наступний за ім’ям функції й списком параметрів (порожнім у цьому випадку) вираз throw() указує, що функція AnyFunction() може збуджувати виняткові ситуації типу Error. Подібне оголошення вказує компіляторові, що функції AnyFunction() не дозволяється збуджувати виняткові ситуації інших типів.

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

void AnyFunction()throw(Overflow, const char*);

 

6. Написання оброблювачів виняткових ситуацій

Приклад:

 

#include <iostream>

#include <cmath>

 

using namespace std;

 

class Error;

 

void Run();

double Pow(double b, double e);

double Power(double b, double e)throw(Error);

 

class Error

{

double b;

double e;

public:

Error()

{

cout<<"Error in source code!"<<endl;

}

Error(double bb, double ee):b(bb),e(ee){}

void Report();

};

 

int main()

{

for(;;)

{

try

{

Run();

cout<<"Program is ending normally"<<endl;

return 0;

}

catch(...)

{

cout<<"Error detected: try again!"<<endl;

}

}

}

 

void Run()

{

try

{

double base, exponent, result;

cout<<"Base?";

cin>>base;

cout<<"Exponent?";

cin>>exponent;

result = Power(base, exponent);

cout<<"Result == "<<result<<endl;

}

catch(Error e)

{

e.Report();

throw e;

}

}

 

double Pow(double b, double e)

{

return exp(e * log(b));

}

 

double Power(double b, double e)throw(Error)

{

if(b>0.0)

return Pow(b, e);

if(b<0.0)

{

double ipart;

double fpart = modf(e, &ipart);

if(fpart == 0)

{

if(fmod(ipart, 2) != 0)

return -Pow(-b, e);

else

return Pow(-b, e);

}

else

throw Error(b, e);

}

else

{

if(e == 0.0)

return 1.0;

if(e < 1.0)

throw Error(b, e);

return 0.0;

}

}

 

void Error::Report()

{

cout<<"Domain error:"

<<" base: "<<b

<<" exponent: "<<e

<<endl;

}

7. Неопрацьовані виняткові ситуації

Якщо в програмі виникли неопрацьовані виняткові ситуації, то за замовчуванням викликається функція unexpected(). Звичайно функція unexpected() викликає функцію terminate(), що, у свою чергу, викликає функцію abort() для аварійного завершення програми. Ви можете замінити функції unexpected() і terminate() своїми функціями-оброблювачами виняткових ситуацій. Замінити функцію abort() не можна.

8. Виняткові ситуації й локальні об’єкти

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

 

int AnyFunction()

{

AnyClass* p = new AnyClass(123);

// . . .

if(condition)

{

delete p;

throw Error();

}

delete p;

return 123;

}

9. Виняткові ситуації й конструктори

class AnyClass

{

OtherClass x;

public:

AnyClass():x(123)

{

if(condition)

throw Error();

}

~AnyClass(){}

};

 

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

 

 


 

Список рекомендованої літератури

 

1. Харви Дейтел. Как программировать на С++ / Х. Дейтел, П. Дейтел; 3-е изд-е, пер. с англ. – М. : ЗАО «Изд-во БИНОМ», 2001. – 1152 с.

2. Глушаков С.В. Язык программирования С++ / С.В. Глушаков, А.В. Коваль,
С.В. Смирнов – Х.: Фолио, 2002. – 500 с.

3. Щедріна О.І. Алгоритмізація та програмування процедур обробки інформації: навч. посібник / О.І. Щедріна. – К.: КНЕУ, 2001. – 240 с.

4. Фаулер М. UML. Основы / М. Фаулер, К. Скотт; – пер. с англ. – СПб: Символ – Плюс, 2002. – 192 с., ил.

5. Леоненков А.В. Самоучитель по UML. [Электронный ресурс]. – Режим доступа: http://khpi-iip.mipk.kharkiv.edu/library/case/index.html.

 

 


 

НАВЧАЛЬНЕ ВИДАННЯ