Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Зубенко, Омельчук - Програмування. Поглиблений курс

.pdf
Скачиваний:
58
Добавлен:
07.03.2016
Размер:
5 Мб
Скачать
☆

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

//повертаємо значення типу Т1, аргументи типів Т1 та Т2

{

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

}

■

Приклад 3.94. Шаблон функції з параметром-класом і парамет- ром-константою:

template <class T, int size> T max1(T (&arr) [size])

{

/*параметризована функція для пошуку мінімального значення в масиві*/

T max-val=arr[0]; for(int i=1; i<size; ++i) if(arr[i]>max-val)

max-val=arr[i]; return max-val;

}

Тут T – тип елементів масиву arr і локальної змінної max-val, а та- кож тип значення, що повертається функцією; size задає розмір ма- сиву ■

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

Приклад 3.95. Шаблон класу:

template<class T1, class T2> class CA{

private: T1* m-pV1; T2 * m-pV2;

public: CA(int n)

{

m-pV1=new T1; m-pV2=new T2;

}

~CA()

{

delete v1;

}

T1 *f1(int n, T2 v);

461

ПРОГРАМУВАННЯ

void f2(T1 v1,T2 v2); };

template<class T1, class T2>

T1 * CA<T1, T2>::f1(int n, T2 v)

{

T1 * p; //…. return p;

}

template<class T1, class T2> void CA<T1, T2>::f2(T1 v1,T2 v2)

{

//….

}

■

У C++ введено стандартну бібліотеку шаблонів STL (англ. – Standard Template Library), що визначає шаблони й функції для векторів (одно- вимірних масивів довільної довжини), множин, асоціативних масивів (map), списків, символьних рядків, потоків введення-виведення тощо.

3.12.2. ПЕРЕТВОРЕННЯ ТИПІВ У МОВІ C++

Виділяють явні й неявні перетворення типів. Неявні істотно не від- різняються від таких у мові C (див. підрозд. 3.2.4). Форма явного пе- ретворення типів, що використовується в мові C, збережена в стан- дарті C++ для сумісності з програмами, написаними мовою C. До того ж стандарт мови C++ пропонує нові операції явного перетворення типів за допомогою специфікаторів static-cast, dynamic-cast, const-

cast та reinterpret-cast.

Перетворення типів виконується для значень змінних при обчис- ленні виразів і впливає на тип результату, але не змінює тип змінних, що містяться у виразі:

<перетворення-типів>::=<cast-name> <тип>(<вираз>); <cast-name>::=static-cast|dynamic-cast|const-cast|reinterpret-cast

Тут <тип> – це тип, до якого зводиться <вираз>. Ця форма явного перетворення типів прийнятніша за таку в мові C, оскільки зазначені оператори містять додаткову інформацію про типи, що беруть участь у перетворенні.

462

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

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

Приклад 3.96. Перетворення типів static-cast:

double d=55.0;

int i=static-cast<int>(d);

■

Крім того, покажчик на void* можна перетворювати на покажчик довільного типу, арифметичне значення – на enum, а базовий клас – на похідний.

Приклад 3.97. Перетворення типів static-cast із використанням void*:

void *p;

int *pi=static-cast <int*>(p);

■

Специфікатор dynamic-cast використовується для динамічного перетворення типів, що реалізується на етапі виконання, і безпечного зведення покажчика на базовий клас до покажчика на похідний

(знижувальне зведення).

Увага! Це єдиний формат перетворення, що перевіряє, чи підтри- мується об'єктом заданий інтерфейс (до якого зводять); якщо ні – то результат 0 ►

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

Приклад 3.98. Перетворення типів dynamic-cast:

class A {...};

class B:public A {...}; class C:public A {...};

void f(A* pa)

{

B* pb=dynamic-cast<B*>(pa); if(pb){

/*якщо pa є об'єктом типу B, то виконується перетво-

рення

і працюємо з об'єктом pb*/

…

463

ПРОГРАМУВАННЯ

}

else {

C* pc=dynamic-cast<C*>(pa); if(pc) {

/*якщо pa є об'єктом типу C, то виконується пе-

ретворення

і працюємо з об'єктом pc*/

…

}

else

{

//pa не є об'єктом класів B чи C

…

}

}

}

■

Специфікатор const-cast виконує зведення константних виразів до неконстантних і навпаки.

Приклад 3.99. Перетворення типів const-cast:

extern char *str-copy(char*); const char *c-str;

char *str=str-copy(const-cast<char*>(c-str));

■

Будь-яке інше використання const-cast викликає помилку на етапі компіляції.

reinterpret-cast – працює з внутрішніми зображеннями об'єктів. Правильність цієї операції цілком залежить від програміста.

Приклад 3.100. Перетворення типів reinterpret-cast:

complex *pCom=reinterpret-cast <complex*>(2000);

■

3.12.3. ДРУЗІ

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

464

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

Дружні класи. У класі можна оголосити інший клас дружнім. Один клас (в якому оголошується друг) надає іншому (другу) можли- вість доступу до всіх своїх закритих елементів.

Отже, якщо клас CA – друг класу CB, то всі його методи можуть зве- ртатися до довільного атрибута чи методу класу CB. Синтаксично це виглядає таким чином:

Приклад 3.101. Дружні класи:

class CB { public:

…

private:

int m-iCount; int f1();

protected: void f2();

…

friend class CA;

}

class CA {

…

}

■

При використанні дружніх класів варто дотримуватися таких правил:

1)У класі мають бути перелічені всі його друзі.

2)Дружній клас може бути оголошений як до, так і після класу, в якому він описаний як друг.

3)Похідні від дружніх класів не успадковують прав спеціального до- ступу.

Можна оголосити також взаємно дружні класи. Приклад 3.102. Взаємно дружні класи:

class CA {

…

friend class CB;

}

class CB {

…

friend class CA;

}

■

465

ПРОГРАМУВАННЯ

Функції-друзі та дружні методи. Функції-друзі – це функції, що не є функціями-членами, але мають доступ до захищених і власних атри- бутів і методів класу. Вони повинні бути описані в тілі класу як friend.

Приклад 3.103. Опис дружньої функції:

Лістинг.

#include <iostream>

using namespace std; class CC {

friend void Show (CC &c1); private:

char *s1; public:

CC() {s1="TEST";};

};

int main()

{

CC c1;

Show(c1);

system("pause"); return 0;

}

void Show (CC &c1)

{

cout<<c1.s1<<'\n';

}

■

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

Приклад 3.104. Опис дружнього методу:

Лістинг.

#include <iostream> using namespace std;

class CE; class CD {

friend void Show (CE &c1); private:

466

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

char *s1; //доступний у класі CD public:

CD() {s1="TEST CD";}; void Show(CE &c1);

};

class CE {

friend void CD::Show (CE &c1); private:

char *s1; //доступний у класах CE та CD::Show public:

CE() {s1="TEST CE";};

};

int main()

{

CD c1; CE c2;

c1.Show(c2);

system("pause"); return 0;

};

void CD::Show (CE &c1)

{

cout<<c1.s1<<'\n';

}

■

3.12.4. ПЕРЕВАНТАЖЕННЯ ОПЕРАЦІЙ

Перевантаження операцій дозволяє визначати дії для об'єктів класу у виразах, що використовують звичайні oперації, такі як +, - тощо.

Наведемо список операцій, що можуть бути перевантажені:

*

/

+

-

%

^

&

|

~

!

,

=

<

>

<=

>=

++

--

<<

>>

==

!=

&&

||

*=

/=

%=

^=

&=

|=

+=

-=

<<=

>>=

->

->*

[]

()

new

delete

Операції +, -, *, & можуть бути перевантажені як для бінарних, так і для унарних виразів. Операції =, (), [], -> потрібно реалізову- вати нестатичними методами.

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

467

ПРОГРАМУВАННЯ

Приклад 3.105. Перевантаження операцій.

Напишемо програму, що використовує перевантаження бінарних операцій +, – та <:

Лістинг.

#include <iostream> using namespace std;

class CA { private:

int m-iX; int m-iY;

public: CA() {

m-iX=0; m-iY=0;

}

CA(int x, int y) { m-iX=x; m-iY=y;

}

CA operator+(CA &a); CA operator-(CA &a); int operator<(CA &a);

void show() {cout<<m-iX<<","<<m-iY<<endl;}

};

//перевантаження +

CA CA::operator+(CA &a)

{

return CA(m-iX+a.m-iX, m-iY+a.m-iY);

}

//перевантаження -

CA CA::operator-(CA &a)

{

return CA(m-iX – a.m-iX, m-iY – a.m-iY);

}

//перевантаження < //тут вважаємо меншим той елемент, в якого сума компонент менша int CA::operator<(CA &a)

{

return ((m-iX+m-iY)<(a.m-iX+a.m-iY));

}

468

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

int main()

{

CA a=CA(6, 2); CA b=CA(3, 4); CA c=CA(0, 0); c=a+b; a.show(); b.show(); c.show();

cout<<"min a, b"<<endl; if(a<b)

a.show(); else b.show(); return 0;

}

У результаті роботи програми на екрані побачимо:

6, 2 3, 4 9, 6

min a, b 3, 4

■

3.12.5. ВИКЛЮЧЕННЯ. БЛОК try-catch-throw

Виключення, або виняткова ситуація (англ. – exceptіon handlіng), –

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

Обробка виключень – це механізм, призначений для обробки поми- лок під час виконання.

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

Для ініціювання виняткової ситуації оператор надсилає об'єкт, що описує її сутність. У мові C++ виняткові ситуації зображуються об'єк- тами: об'єкт може бути літеральним значенням, рядком, об'єктом кла- су тощо. При ініціюванні виняткової ситуації одразу завершується виконання функції.

469

ПРОГРАМУВАННЯ

Приклад 3.106. Використання оператора throw, що ініціює ви- ключення:

class CTestError { public:

CTestError(){};

~CTestError(){};

const char *showReason() const {return "Exception in CTestError class.";}

};

…

int fun(int i)

{

if(i==0) //якщо виконується умова

throw 1;//то ініціюється виняткова ситуація типу int if(i==1)//якщо виконується умова, то

throw CTestError();//надсилає об'єкт класу CTestError //якщо немає проблем, то

return i;

}

Ця функція повертає цілочислове значення лише у випадку, коли не виникли виняткові ситуації; якщо така виникла, то вона повертає об'єкт CТestError ■

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

Приклад 3.107. Використання оператора catch:

catch(CTestError e) {

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

catch(int) {

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

■

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

Приклад 3.108. Використання try …catch:

int i=0;

…

try {

470