Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекції 1-8.docx
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
6.11 Mб
Скачать

Лекція 7

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

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

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

Бажано використовувати раннє зв'язування, а пізнє використо­вувати з потреби. У «суто» об'єктно-орієнтованих мовах, напри­клад, SmallTalk, використовується тільки механізм пізнього зв'я­зування.

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

Раннє зв 'язування або перевантаження. У програмуванні, ран­нє зв'язування може застосовуватися як для закритих підпрограм, так і для операцій і операторів. У мовах процедурного програму­вання поліморфізм визначається для наперед визначених конструк­цій. У об'єктно-орієнтованих мовах перевантаження може визнача­тися як для підпрограм, так і операцій (операторів). Вибір форми програмної конструкції при ранньому зв'язуванні визначається результатом аналізу інтерфейсу (списку параметрів програмної конструкції).

Перевантаження підпрограм. Реалізація підпрограми вибира­ється під час компіляції. Наприклад, у мові C++:

class abs_val

{

public:

int number(int val)

{

return(abs(val));

}

double number(double( val))

{

return(fabs(vall));

}

};

main()

{

int му Vallnt;

double my ValDouble; abs_val myabs;

my Vallnt = my_abs.number(--10)

my ValDouble = my_abc.number(- 10.0);

}

У класах, окрім конструкторів, можуть бути перевантажені

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

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

У мовах С++ і С# на механізмі раннього зв'язування будується перевантаження операцій і операторів. Цей тип перевантаженн вводиться в мову і використовується з метою скорочення позна­чень підпрограм, що реалізовують відомі в мові операції і операто­ри для типів. Наприклад, використання позначення операції скла­дання «+» для позначення операції над значеннями індексованого типу. Як позначення операцій або операторів беруться позначення визначених у мові операцій або операторів (+, - /, », « =). Більшість операцій можна перезавантажувати. і

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

Старшинство і асоціативність операцій не можуть бути змінені перевантаженням. Створювати нові позначення операцій не можна.

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

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

class Ex {

public:

void Value (int v)

{

value = v;

}

int Value ()

{

return Value;

}

private:

int value;

};

void main ()

int nl;

Ex obj;

obj.value (10);

nl = obj. value ()

}

Механізм перевантаження Наприклад, у мові C++:

class Ех 1

{

public:

int add(int і) {

return(i+l);

}

};

Class Ех2: public Ex 1

{

public:

int add(float i)

{

return(i+10);

}

};

void main () {

int nl;

Ex2 obj;

nl = obj.add(2);

}

У цьому прикладі буде викликана підпрограма add, яка описам в похідному класі Ех2. Оскільки мова C++ не точно типізована, то після приведення типів відбудеться виклик підпрограми. У мовах що точно типізуються, буде видано повідомлення про ПОМИЛКУ невідповідності типів. Застосовуючи оператор розширення види-мості («::»), можна виконати підпрограму, описану в базовому кла­сі, наприклад, Exl:: add(2).

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

Якщо операція не повинна змінити значення результату виконання оператора, то для розміщення результатів використовуються локальні об’єкти типу відмінного від класу. Є оператори які повертають значення відмінне від типу (наприклад операції відношення і логічні). Для позначення перевантаження застосовують ключове слово operator. Наприклад перевантаження в мові С++

Class coord

{

public:

coord()

{

x=0;

y=0;

}

coord (int i, int j)

{

x = i;

y=j;

}

void get_xy(int & i, int & j)

{

i=x;

j = y;

}

coord operator + (coord obj);

private:

int x,y;

};

coord coord :: operator + (coord obj)

{

coord temp;

temp.x = x + obj.x;

temp.y = y + obj.y;

return temp;

}

void main ()

{

coord ob01(10,10);

coord ob02(5,3);

coord ob03 ();

{

nt x, y;

ob03 = ob01 +ob02

getxy(x; y)

}

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

«дружні» підпрограми. Наприклад, у такому тексті в мові C++:

Class А

{

public:

A operator + (А);

private:

int val;

};

А А :: operator + (A m_x)

{

А А1();

А А2(3);

Al = А2 + «xyz»; Al = «xyz» + A2;

}

Тут, в останньому операторі привласнення компілятор виявить синтаксичну помилку, оскільки тип першого операнда не І типом класу А. При цьому перетворення за умовчанням не дозволяється. Якщо перевантажений оператор описати як «дружній», наприклад:

frend A operator + (A m х, А т_х)

{

return A(m_x.val = m__y.val);

};

то для першого операнда буде виконано неявне приведення типів.

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

Public class Coord

public Coord()

{

x = 0;

y = 0:

}

public Coord(int i, int j)

{

x = i;

y =j;

}

public Coord Add(Coord obj)

{

Coord tmp = new Coord();

tmp.x = x + obj.x;

tmp.y = y + obj.y;

return tmp;

}

public static Coord operator +(Coord obj 1, Coord obj2)

{

Coord tmp = new Coord();

tmp.x = obj 1 .x + obj2.x;

tmp.y = obj 1 .y + obj2.y;

return tmp;

}

private int x;

private int y;

}

static void Main(string[] args)

{

Coord o1 = new Coord(4,5); // o1 = {4, 5}

Coord o2 = new Coord( 1,1); //o2 = {l, 1}

Coordo3 = ol.Add(o2); //o3 = {5,6}

// або

o2 = ol + o2; //o2 = {5,6}

}

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

class А

{

public

А (int і)

{

val = і

А Operator !();

{

return A (!val);

}

private:

int val;

};

void main ()

{

A obj(3);

obj = !obj;

}

Очевидно, що можна одночасно перезавантажувати і унарні бінарні операції.

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

Наприклад:

public static Coord operator ! (Coord op)

{

op.x = -op.x;

op.y = -op.y;

return op;

}

В умовних виразах структурних операторів у мові С# на відмі­ну від мови C++ необхідно явно використовувати операцію відно­шення («і» щ помилка, «і = = true» - правильно).

Особливості перевантаження операторів і операцій. Всі пере­вантажені оператори реалізуються як функції. Деякі оператори мають - арність, що збігається з - арністю визначених операторів. Інші мають особливості. Наприклад, операція індексування («[]») в мові C++ теж розглядається тільки як бінарний оператор такого вигляду:

long operator [] (char*);.

Навпаки, оператор виклику підпрограми «()» в мові C++ може

мати більше двох параметрів. Наприклад:

class А

{

public:

int х operator () (int x, int y, int z)

{

return value [x] [y] [z];

}

private:

im value[10][10][10];

};

Void main()

{

A obj;

intx;

int у;

int z;

x = 2;

У = 2;

z = 2

obj (x, y, z) = obj(x + 2, у - 1, z)+ obj(x, y, z): } і

У цьому прикладі, в операторові привласнення використову­ється операція «+» для визначеного типу int.