Термінова допомога студентам
Дипломи, курсові, реферати, контрольні...

Об'єктивне програмування

РефератДопомога в написанніДізнатися вартістьмоєї роботи

Працюючи зі структурами великого розміру — під час передачі їх як параметрів і результатів функцій — копіювання їх є неефективною операцією. Значно ефективнішим є передавати посилання цю структуру. А, щоб постійно не вказувати операції взяття адреси й опосередкованого звернення з засланні в Сі++ запроваджено тип — неявна посилання: щодо перемінної неявно вводиться посилання, яка вказує з цього… Читати ще >

Об'єктивне програмування (реферат, курсова, диплом, контрольна)

ЗАПРОВАДЖЕННЯ У ОБ'ЄКТНЕ ПРОГРАММИРОВАНИЕ Лекция 1. Об'єктне програмування як технологія програмування ——————————————————————————————;

Традиційна технологія програмування 1970;х років — структурне программирование:

— модульне программирование;

— спадне программирование;

— структурне проектування процедур та об'єктивності даних (програмування без goto).

Мова Паскаль — відповідає зазначеним принципам був розроблений під впливом ідей структурного программирования.

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

Наступний крок — введення у програму об'єктів. Під об'єктом розуміється структура даних, що містить повну інформацію про стан відповідного фізичного об'єкта, який відображається програмою. У Сі цьому може відповідати структура struct), в Паскале — запис (record). Безліч об'єктів одного типу становлять поняття класу. Объектно-ориентированный підхід до розробки програм передбачає, що у програмі встановлюється взаимно-однозначное відповідність між фізичними об'єктами, отображаемыми програмою, і програмнными об'єктами, можуть бути, по суті, структурованими перемінними (надалі під терміном «об'єкт «усвідомимо програмний объект).

Традиционный підхід: ————— змінна тип данных Объектно-ориентирофізичний програмний клас ванний підхід: об'єкт об'єкт объектов.

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

Об'єктно-орієнтовані програми можна робити і з допомогою традиційних мов програмування. Розглянемо приклад визначення об'єктів типу «дата «на класичному Си.

//——— структура dat — аналог класу об'єктів «дата «————typedef struct dat.

{ unsigned day; unsigned month; unsigned year;

}.

DAT; //——- набір функцій для класу об'єктів «дата «———————static int mm[] = {31,28,31,30,31,30,31,31,30,31,30,31}; //——- Перевірка на коректність ——————————————-int TestData (p) DAT *p; { if (p->month ==2 && p->day==29 && p->year %4 ==0) return (1); if (p->month ==0 || p->month >12 || p->day ==0 || p->day >mm[p->month]) return (0); return (1); } //——— Наступна дата ————————————————————void NextData (p) DAT *p; { p->day++; if (p->day month]) return; if (p->month ==2 && p->day==29 && p->year %4 ==0) return; p->day=1; p->month++; if (p->month ≠13) return; p->month=1; p->year++; } //———- Наступна дата через n днів —————————————void PlusData (p, n) DAT *p; int n; { while (n— ≠0) NextData (p); } //———- Основна програма ————————————————- void main () { DAT a; do.

{ scanf («%d%d%d », &a.day, &a.month, &a.year);

} while (TestData (&a) ==0); PlusData (&a, 17); } //———————————————————————————-;

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

Мова програмування Сі++ є розширення мови Сі для програмування об'єктів та його класів. У цьому використання класів еквівалентно до синтаксису використанню базових типів данных:

Поняття класичного Сі Поняття Си++.

————————————- —————- БТД:

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

Об'єкт: область пам’яті, яка містить змінна, що містить структуру даних певного структуру даних, певну типу. як клас. ————————————————————————————- Операция:

Перевизначення операторів: операція над перемінної інтерфункція, певна для объек претируется стосовно тому тов зазначеного класу то, можливо БТД, до якої належить перевикликана у вигляді одного з стандарт менная (так операція «+ «ных операцій мови Сі, яка різниться в інтерпретаціях для переопределяется, якщо операндом змінних типу int і double). став об'єкт класу, а чи не змінна БТД.

Лекція 2. Додаткові можливості мови Си++.

———————————————————————;

Нижче розглянемо кошти, розширюють класичний Сі. Хоча які й не ставляться безпосередньо до класам, з допомогою можна реалізувати розглянуті вище принципи объектно-ориентированного программирования.

2.1. Присвоювання структур

————————————- Операція присвоювання то, можливо застосована до структур одного типу. І тут передбачається їх побайтное копіювання одній на іншу. Вона (а чи не посилання неї) може статися фактичним параметром і результатом функції. Якщо є на структуру безпосередньо з ім'ям p, то результатом операції *p є структура в цілому. Отже, структура наближається до базовим типам даних у цьому сенсі, що нею можливі вищевказані операції. Для позначення структури можна також ознайомитися використовувати ім'я структури без ключового слова struct.

struct dat.

{ int day, month, year; }.

dat NextDat (dat x) // Формальний параметр — структура { … return (x); } // Повернути структуру як результат.

dat Nextdat1(dat *p) { … return (*p); } // Повернення структури побічно по ссылке.

dat a, b, c,*q; // Ключове слово struct немає void main () { q = &b; a = b; // Пряме присвоювання структур a = *q; // Непряме присвоювання по засланні з = NextDat (b); // Присвоювання структури як результату з = NextDat1(&b); // функції, фактичний параметр в } // NextDat — копія структуры.

2.2. Звернення за адресою (неявна ссылка).

———————————————————-;

Працюючи зі структурами великого розміру — під час передачі їх як параметрів і результатів функцій — копіювання їх є неефективною операцією. Значно ефективнішим є передавати посилання цю структуру. А, щоб постійно не вказувати операції взяття адреси й опосередкованого звернення з засланні в Сі++ запроваджено тип — неявна посилання: щодо перемінної неявно вводиться посилання, яка вказує з цього зміну. Використання цієї перемінної переважно операцій передбачає непряме звернення з відповідної засланні. При ініціалізації такий перемінної значенням інший перемінної створюється посилання з цього іншу зміну. З використанням у кожному вираженні перемінної - неявній посилання реально виробляється непряме звернення з створеної ссылке.

Си++ Еквівалент в «класичному «Сі ———————————— ——————————————-//——————- — Ініціалізація константою ————————-int &a = 5; int a, *pa =a;

*pa = 5; //———————- Ініціалізація перемінної ————————-int x; int x,*pa; int &a = x; pa = &x;

a = 5; *pa = 5; //——————— Неявна посилання структуру ————————struct dat struct dat { int day, month, year }; { int day, month, year }; dat x; dat x; dat& b = x; dat* pb = &x; dat& з = {12,12,1990}; dat cc = {12,12,1990}; dat *pc = &cc;

b.year = 1990; pb->year= 1990; c. day=b.day+3; pc->day = pb->day+3; з = b; // Копіювання pc->day = pb->day;

// структури pc->month = pb->month; pc->year = pb->year;

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

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

— значением;

— явною ссылкой;

— неявній ссылкой.

Пример 1. Параметри — значення ————————————————————————————-dat Inc (dat x).

========> - копіювання { ————> - посилання x. day++; return (x); —— стік +—-+x.day++ } ¦ b =========> x =========.

L—— +—-+ ¦ return (x) void main () ¦ { —— стік +—-+ —¦- тимчасова dat a, b,*p; ¦ a day++ x->day++; —— стік +—-+ return (x); р===== b ¦ a = *Inc (Inc (&b)); ¦ ¦ L—— ±¦-+ p = Inc (&b); ¦ ¦ —— ¦return (x) a = *p; ¦ L— a ¦

L—- Приклад 3. Параметри — неявні посилання ————————————————————————————-dat& Inc (dat& x) x. day++ неявна посилання dat* px { x. day++ x. day++; —— стік +—-+ return (x); р===== b ¦ a = Inc (Inc (b)); ¦ ¦ L—— ±¦-+ p = &Inc (b); ¦ ¦ —— ¦return (px) a = *p; ¦ L— a ¦

L—;

Порівняння цих прикладів показує следующее:

— під час роботи з формальним параметром — неявній посиланням використовується ім'я формального параметра як ідентифікатора перемінної, яка замінюється транслятором на непряме звернення з неявній ссылке;

— при поверненні результату використовується ім'я переменной, которая замінюється транслятором неявній посиланням на нее;

— приклади 2 і трьох ідентичні щодо реалізації, але відрізняються по синтаксису виклику функции;

— приклади 1 і трьох відрізняються щодо реалізації, але ідентичні по синтаксису виклику функции;

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

Оскільки розмір структури, переданої як результату функції, то, можливо як завгодно великим, то тут для зберігання необхідно створити тимчасову зміну. Транслятор «Borland З «у разі надходить наступним образом:

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

— і під час операції return (x), де x — локальна змінна чи формальний параметр, виконується побайтовое копіювання перемінної x по адресою, заданому неявним параметром;

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

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

Программа на Сі++ Реалізація ————————- ————— — неявний параметр dat Inc (dat x) void Inc (dat *r, dat x) { { x. day++; x. day++; return (x); *r = x; // Копіювання } } // результата.

void main () void main () { { dat a, b*p; dat a, b,*p; dat t, u; // Временнye переменнye a = Inc (b); Inc (&a, b); // Посилання на ліву частина p = &Inc (b); Inc (&t, b); // Присаивание тимчасової p = &t; // перемінної й одержання a = *p; a = *p; // посилання неї a = Inc (Inc (b)); Inc (&u, b); // Проміжний результат.

Inc (&a, u); // в тимчасовій перемінної } }.

2.3. Функції - елементи структуры.

———————————————— Повторимо розглянутий вище приклад на другий форме:

//—————— структура dat — аналог класу об'єктів «дата «—struct dat.

{ unsigned day; unsigned month; unsigned year; int TestData (); void NextData (); void PlusData (int n).

{ while (n— ≠0) dat: NextData (this);

}.

}; //—————- набір функцій для класу об'єктів «дата «————static int mm[] = {31,28,31,30,31,30,31,31,30,31,30,31}; //—————- Перевірка на коректність ———————————-int dat: TestData () { if (month ==2 && day==29 && year %4 ==0) return (1); if (month ==0 || month >12 || day ==0 || day >mm[month]) return (0); return (1); } //—————- Наступна дата —————————————————void dat: NextData () { day++; if (day month = 5; this->day++;

или неявно.

month = 5; day++;

— для перемінної, має тип деякою структури, виклик функцииэлемента цієї структури має вид.

. ().

2.4. Перевизначення функций.

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

overload SetDat; void SetDat (int dd, int mm, int yy, dat *p).

{ // Дата вводять у вигляді трьох цілих p->day=dd; p->month=mm; p->year=yy;

} void SetDat (char *s, dat *p) // Дата вводять у вигляді строки.

{ sscanf (s, «%d%d%d », &p->day, &p->month, &p->year);

}.

void main ().

{ dat a, b;

SetDat (12, 12, 1990, &a); // Виклик першої функции.

SetDat («12,12,1990 », &b); // Виклик другий функции.

}.

Функции-элементы також може бути перевизначені, у своїй явного оголошення не требуется.

struct dat.

{ int day, month, year; void SetDat (int, int, int); void Setdat (char *);

} void dat: SetDat (int dd, int mm, int yy).

{ day=dd; month=mm; year=yy;

} void dat: SetDat (char *s).

{ sscanf (s, «%d%d%d » ,&day,&month,&year);

} void main ().

{ dat a, b; a. SetDat (12,12,1990); b. SetDat («12,12,1990 »);

}.

2.5. Оператори управління динамічної памятью.

——————————————————————;

У бібліотеці Сі є дві функцій управління динамічної пам’яттю — malloc () і free (), які виділяють і звільняють область пам’яті заданого розміру (в байтах). У цій сфері програма може розмістити зміну (чи масив), що називається динамічної. При виділенні пам’яті під динамічну зміну необхідно з допомогою операції sizeof визначати кількість байтів, необхідне розміщення перемінної зазначеного типу. У Сі++ запроваджені два оператора, аналогічні функцій malloc і free new і delete. Вони від відповідних функцій тим, що допускають використання газу як аргументів безпосередньо специфікацію типу створюваної динамічної перемінної й посилання на динамічну переменную:

Си++ «Класичний «Сі ————————————- ————————————————-char *s, x[80]; char *s, x[80]; dat *p,*q; struct dat *p,*q; void main () void main () { { p = new dat; p = malloc (sizeof (struct dat)); q = new dat[15]; q = malloc (15*sizeof (struct dat)); gets (x); gets (x); p. s = new char[strlen (x)+1]; p. s = malloc (strlen (x)+1); … … delete p; free (p); delete q; free (q); delete p. s; free (s); }.

Оператори мають вид:

delete.

2.6. Параметри функцій по умолчанию.

————————————————-;

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

//——- Функція встановлює за умовчанням поточне значення року, //——- місяці та дня #include.

void dat: SetDat (int d=0, int m=0, int y=0) { struct date x; getdate (&x); // Стандартна функція получения.

// поточної даты.

// Перевірка на значення за умовчанням year = (y == 0)? x. da_year: y; month= (m == 0)? x. da_month: m; day = (d == 0)? x. da_day: d; }.

2.7 Контроль перетворення типів ссылок.

———————————————————;

У «класичному «Сі і під час присвоювання, передачі фактичних параметрів відбувається автоматичне перетворення посилань до базовим типам даних (int, unsigned) і навпаки, і навіть перетворення одного типу посилання до іншого. У Сі++ такі «вільності «виключені, програміст повинна сама виконати явне перетворення. Наприклад, при використанні функції розподілу динамічної пам’яті, має прототип в «alloc.h «.

extern void* malloc (int n);

dat *p;

p = (dat *) malloc (10*sizeof (dat));

¦

L—- перетворення void* в dat*.

Природно, що це перетворення типів фіктивне тому, що ні змінює значення посилання і призводить до генерації коду. Воно лише змінює «думку «транслятора цю ссылку.

2.8 Вставляемые (inline) функции.

———————————————;

Якщо функція (звичайна чи элемент-функция структури чи класу) оголошено inline-функциями, то, при виклик таких функцій транслятор виконує підстановку за текстом програми тіла функції із відповідною заміною формальних параметрів на фактичні. Элемент-функция також вважається inline за умовчанням, коли його тіло визначено у визначенні структури (чи класса), например:

struct dat.

{ int d, m, y; void Setdat (char *p) // Функція inline по умолчанию.

{.

… // Тіло функции.

}.

2.9 Посилання на елементи структуры.

———————————————;

Якщо структура має низку елементів одного типа, то нею може бути створена «внутрішня «посилання, яка значення внутрішнього адреси (усунення) елемента щодо обраної структури. Формування й використання такої посилання ясно з примера:

struct dat.

{ int day, month, year; void Getdat (); void Putdat (); void Nextdat ();

}.

int dat:*p; // Посилання на елемент типу int.

// у структурі dat p = & dat: month; // Значення p — усунення (адрес).

// елемента month у структурі типа.

// dat.

dat x,*px = &x; //.

x.*p = 5; // Звернення по внутрішньої засланні px->*p = 5; //. *.

// -> * Еквівалентно x. month = 5; px->month =5;

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

void (dat:*fun)(); // Посилання на элемент-функцию.

// структури dat.

fun = & dat: Putdat (); // Значення fun — посилання на.

// элемент-функцию Putdat в dat (x.*fun)(); // Виклик элемента-функции по (px->*fun)(); // засланні fun для структури x.

// й у структури по засланні px Еквівалентно x. Putdat (); px->Putdat ();

2.10 Незмінні перемінні (константы).

———————————————————;

У Сі++ запроваджено додатковий контролю над зміною значень змінних. Ключове слово const, використовуваної щодо і ініціалізації перемінної, забороняє зміну, що контролюється транслятором у її подальше використання. Така сама можливість є і для формальних параметрів функції, например:

const int n=5; n++; // Запрещено.

int xxx (const int m).

{ m++; // Запрещено.

}.

Що стосується засланні const можна використовувати у двох варіантах, стосовно самої засланні (адресою) і стосовно указуемому значению:

— під час використання conts стосовно указуемому значенням дозволяється модифікувати саму заслання з допомогою присвоювання і операцій адресної арифметики, а зміни операнда побічно по засланні заборонені. Така посилання називається посиланням на постійний объект:

const char * p; p = «1 234 567 890 »; // Дозволено присвоювання засланні p + =3; // Дозволена модифікація ссылки.

*(p+3) = «3 »; // Заборонено присвоювання по ссылке.

(*p)++; // Заборонено приріст по ссылке.

— під час використання const стосовно засланні забороняється змінювати значення посилання після ініціалізації, зокрема засобами адресної арифметики. Така посилання називається постійної посиланням на объект:

char const* p = «1 234 567 890 »; char c;

(*p) = «3 »; // Дозволено присвоювання по ссылке.

p++; // Заборонено зміна значення з = *(p+3); // самої ссылки.

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

const char const* p = «1 234 567 890 » ;

2.11 Загальні зауваження доповнення в Си++.

———————————————————-;

Основна особливість Сі++ від «класичного «Си:

— структура (struct) наближено як до базовим типам даних (char, int);

— уведено поняття элемента-функции. Элементы-функции грають роль своєрідного «інтерфейсу «від використання певної програмістом структуры;

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

Всі ці нових властивостей необхідні щодо понять класу тут і объекта.

Лекція 3. Класи. Об'єкти. Конструктори і деструкторы.

—————————————————————————-;

3.1.Понятие класу тут і об'єкта в Си++.

————————————————-;

У найпростішому вигляді клас визначається Сі++ як структура, роботу з елементами якої можливе тільки через элементы-функции. На відміну від структури клас має «приватну «(особисту) частина, елементи якої може бути доступні інакше ніби крізь інші элементыфункции, і «публічну «(загальну) частина, елементи якої можна використовувати безпосередньо. Об'єктом називається обумовлена у програмі змінна, тип якої визначено як клас (структура):

Определение структури Визначення класу ————————————- —————————————————-struct dat class dat { { // Приватна частина int day, month, year; int day, month, year; public: // Публічна частина void SetDat (int, int, int); void SetDat (int, int, int); void SetDat (char *); void SetDat (char *); } аа} void main () void main () { { // Опред-ние змінних a, b // Опред-ние об'єктів a, b класу dat dat a, b; dat a, b; a. day = 5; // Безпосереднє використання a. month = 12; // приватній частини об'єкта заборонено bAA. SetDat («12,12,1990 »); b. Setdat («12,12,1990 »); } }.

" Приватна «частина класу необов’язково повинна потрапляти початку визначення класу. Для її позначення в довільному місці визначення класу можна використовувати службове слово private.

Tаким чином у першому наближенні клас відрізняється від структури чітко певним інтерфейсом доступу для її елементам. Об'єкти класу мають усіма властивостями змінних, зокрема такими, як область дії і клас пам’яті (тривалість життя). Останнє властивість найцікавіше, оскільки процеси створення і знищення об'єктів класу можуть супроводжуватися викликом функцій (конструктор і деструктор). Нагадаємо, що у класам пам’яті (і часу життя) в Сі різняться переменные:

— статичні (зовнішні), створювані в статичної пам’яті програми розвитку й що у протягом всієї роботи программы;

— автоматичні, створювані в стеці в останній момент виклику функції і знищувані у її завершении;

— динамічні, створювані і знищувані у вільному пам’яті завдання у моменти виклику функцій malloc () і free () чи виконання операторів new і delete.

Відповідно, у програмі можливо визначення статичних, автоматичних і динамічних об'єктів одного класса:

class dat.

{ … } dat a, b; // Статичні об'єкти dat *p; // Посилання на об'єкт void main () { dat c, d; // Автоматичні об'єкти p = new dat; // Динамічний об'єкт … delete p; // Знищення динамічного об'єкта } %2d-%2d-%4dn ", day, month, year); } //———————————————————————————dat a («12−12- 1990 »); // Зовнішня змінна — конструктор

// викликається перед main () dat b[10]; // Масив об'єктів — конструктор без.

// параметрів викликається перед main () void xxx (dat &p) { dat c (12,12); // Викликається Конструктор dat (int, int, int).

// для автоматичного об'єкта dat d = p; // Конструктор для автоматичного об'єкта не … // викликається, т.к. об'єкт инициализируется … // копіюванням } // При виході з функції викликаються деструкторы.

// для об'єктів з і d void main () { int i, n; scanf («%d » ,&n); dat *p = new dat[n]; // Створення масиву динамічних объектов.

// конструктор без параметрів явно викликається for (i=0; inext=this; lst=this; } } //———————————————————————————-void list: extract () { list *p,*pred; // Пошук поточного і попереднього for (pred=NULL, p=fst; p ≠NULL; // у списку pred=p, p=p->next) if (p=this) break; // Якщо знайдено — вихід if (p ≠NULL).

{ // Найден — виключення зі списку if (pred==NULL) fst = next; else pred->next=next;

} } //———————————————————————————-void list: show () { list *p; for (p=fst; p ≠NULL; p=p->next).

{ …висновок інформацію про об'єкті… } } //——— Під час створення об'єкта він міститься у список —————- list: list () { insfst (); } //——— При знищенні об'єкта він виключається зі списку ——— list:~list () { extract (); }.

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

Статичними можуть бути оголошені ще й элементы-функции. Їх «статичність «залежить від того, що виклик їх пов’язані з конкреетным об'єктом і можуть виконати по повного імені. Відповідно, у них використовуються неявна посилання this. Вони запроваджуються, зазвичай, для виконання дій, относящихсмя до всіх об'єктів класу. Для попереднього примера.

class list.

{ … static void show (); // Стaтическая функція просмотра.

} // всього списку об'єктів //———————————————————————————-static void list: show () { list *p; for (p=fst; p ≠NULL; p=p->next).

{ …висновок інформацію про об'єкті… } } //———————————————————————————-void main () { … list: show (); // Виклик функції повного імені }.

Лекція 4. Перевизначення операторов.

—————————————————-;

Нагадаємо, під класом розуміється визначається програмістом тип даних, використовуваний нарівні зі стандартними базовими типами. З точки зору «рівноправності «знову який вводимо типу даних бажано мати можливість розширення (перевизначення) операцій мови, де чи кілька операндов може бути об'єктами цього Це досягається запровадженням элемента-функции спеціального виду, звернення до котрої я компілятор формує при трансляції цієї операції. Природно, що ця функція повинен мати результат (значення чи неявна посилання), якщо передбачається використання цієї операції всередині іншого выражения.

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

Слід зазначити ще й те що, що з кожної комбінації типів операндов переобумовленої операції необхідно провести окрему функцію, тобто транслятор неспроможна виробляти перестановку операндов місцями, навіть якщо базова операція допускає це. Наприклад, при перевизначенні операції складання об'єкта класу dat із необхідно дві функції dat+int і int+dat.

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

operator ().

У цьому ім'я функції складається з ключового слова operator і символу цієї операції в синтаксисі мови Си.

Список формальних параметрів функції є списком операндов (кількість, типи, способи передачі) операции.

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

Є два засобу опису функції, відповідної переобумовленої операции:

— якщо функція задається як звичайна элемент-функция класу, то першим аргументом відповідної операції є об'єкт, посилання який передається неявним параметром this;

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

Як приклад розглянемо доопределение стандартних операцій над датами.

#include #include #include.

static int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };

class dat.

{ int day, month, year; public: void next (); // Элемент-функция вычисления.

// наступного для dat operator++(); // Операція ++ dat operator+(int); // Операція «дата + ціле «.

// з неявним операндом через this friend dat operator+(dat, int) // Операції з явною передачею friend dat operator+(int, dat) // всіх параметрів за значенням dat (); // Конструктори dat (int, int, int); // (див. попередні приклади) dat (char *); //.

~dat (); // Деструктор

}; // (див. попередні примеры).

//——— Функція обчислення наступного дня ——————————// Використовується посилання поточний об'єкт this, // який изменяетсмя у процесі операції //———————————————————————————-void dat: next () { day++; if (day > days[month]).

{ if ((month==2) && (day==29) && (year%4==0)) return; day=1; month++; if (month==13).

{ month=1; year++;

}.

} } //——— Операція инкремента дати ——————————————-//1. Форма элемента-фукнции з неявним операндом по засланні this //2. Повертає копію вхідного об'єкта (операнда) до збільшення //3. Відповідає операції dat++ (збільшення після використання) //4. Зауваження: для унарных операцій типу — чи ++ використання // їх до чи влітку після операнда має значення (викликається одна // й та функція). //———————————————————————————- dat dat: operator++() {.

// Складається тимчасовий об'єкт dat x = *this; // До нього копіюється значення поточного об'єкта dat: next (); // Збільшується значення поточного об'єкта return (x); // Повертається тимчасовий об'єкт }.

//——— Операція «дата + ціле «———————————————//1. Елементфункція з неявним першим аргументом по засланні this //2. Вхідний об'єкт не змінюється, результат повертається копією // внутрішнього автоматичного об'єкта x //———————————————————————————-dat dat: operator+(int n) { dat x; x = *this; // Копіювання поточного об'єкта в x while (n— ≠0) x. next (); // Виклик функції next для об'єкта x return (x); // Повернення копії об'єкта x }.

//——— Операція «дата + ціле «———————————————//1. Дружня элемент-функция які з списком аргументів //2. Альтернативний варіант попередньої функції //3. Перший операнд класу dat — передається за значенням, // тому може модифікуватися без зміни вихідного об'єкта //———————————————————————————-dat operator+(dat p, int n) { while (n— ≠0) p. next (p); // Виклик функції next для об'єкта p return (p); // Повернення копії об'єкта x }.

//——— Операція «ціле + дата «——————————————-//1. Дружня элемент-функция які з списком аргументів //2. Другий операнд класу dat — передається за значенням, //тому може модифікуватися без зміни вихідного об'єкта //———————————————————————————-dat operator+(int n, dat p) { while (n— ≠0) p. next (); // Виклик функції next для об'єкта p return (p); // Повернення копії об'єкта p }.

//———————————————————————————- void main () { int і; dat a; dat b (17,12,1990); dat c (12,7); dat d (3); dat e; dat *p = new dat[10]; clrscr (); e = a++; d=b+15; for (i=0; і 0; month++) // Віднімання днів по месяцам.

{ p -= days[month]; if (month == 2 && year % 4 == 0) p—;

} month—; // Відновлення останнього p += days[month]; // місяці if (month == 2 && year % 4 == 0) p++; day = p + 1; }.

void main () { dat a («12−05−1990 »); // Дата, задана текстовій рядком dat b; // Поточна дата int з; long d;

// Явна перетворення до long printf («З 12−05−1990 минуло %4ld днейn », (long)b-(long)a);

// Явна перетворення до int printf («Цього року минуло %3d днейn », (int)b); з = b; // Неявний перетворення при привласненні d = b — a; // Операція dat-dat.

printf («З 12−05−1990 минуло %4ld днейn », d); printf («Цього року минуло %3d днейn », з); }.

5.3 Перевизначення операцій new і delete.

———————————————————-;

Операції створення і знищення об'єктів в динамічної пам’яті можуть бути перевизначені наступним образом:

void *operator new (size_t size); void operator delete (void *);

где void * - посилання область пам’яті, виділювану під об'єкт, size — розмір об'єкта в байтах.

Перевизначення операцій дозволяє написати власне розподіл пам’яті для об'єктів класса.

5.4 Перевизначення операцій [], (), ->

——————————————————— Перевизначення (): —————————-class one.

{ public: typeout operator ()(type1,type2);

};

Вызов: type1 a; // Виклик оператора збігаються з type2 b; // синтаксисом виклику функції one obj; // безпосередньо з ім'ям даного объекта.

… obj (a, b) … еквівалентно obj. operator ()(a, b).

Переопределение ->: —————————class two.

{ public: type Y;

};

class one.

{ two operator->(); // Операція повинна повертати об'єкт чи two* operator->(); // чи посилання об'єкт класу two,.

}; // у якому визначено елемент Y.

Вызов: one obj;

… obj->Y. еквівалентно (obj.operator->()) ->Y.

Переопределение []: використовується для моделювання віртуальних —————————— масивів елементів певного типу. class text_page.

{ char **page; // Масив посилань на рядки public: int operator[](char*); // Асоціативний пошук индекса.

// по рядку char* operator[](int); // Виділення рядки по индексу.

};

5.5 Перевизначення операції копіювання объектов.

———————————————————————-;

Kaк відомо, визначення об'єкта класу в виде.

=.

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

Taкое копіювання некоректно у разі, якщо об'єкти містять посилання інші об'єкти чи перемінні в динамічної пам’яті. У цьому вся разі можна воспъльзоваться спеціальним конструктором копіювання, параметром котрого є неявна посилання об'єкт — джерело, а this свідчить про об'єкт приймач. Будучи певним, він викликається переважають у всіх перелічених вище випадках копіювання об'єктів як другой.

Приклад коректного конструктора копіювання для класу рядків має вид:

class string.

{ char *p.s; // Посилання на рядок int sz; // Довжина рядки public: string (string&);// Конструктор копирования.

}; // створює копію рядки у динамической.

// пам’яті для об'єкта — приймача string: string (string& right).

{ p. s = new char[right->sz]; strcpy (s, right->s);

}.

Лекція 6. Похідні классы.

—————————————;

6.1 Вкладені классы.

—————————;

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

Перший випадок є звичайний спосіб побудови инрархической структури даних, щоб старого класу одна із елементів даних «приватній «частини нового класу. Вона має власне ім'я (іменовано), яким щодо нього можна звертатися як об'єкта. У элементах-функциях нового класу можна використовувати элементы-функции та постійні операції для об'єкта старого класу. Розглянемо за приклад клас man — інформацію про людині, куди входять у собі дати його й надходження на работу.

class man.

{ char name[20]; // Інші елементи класу char *address; dat dat1; // Дата народження dat dat2; // Дата надходження працювати public: void newadr (); // Элемент-функция man (char*); // Конструктор

}.

//——- Функція «Змінити адресу проживання «————————void man: newadr ().

{ int n; char s[80]; // Рядок нового адреси if (address ≠ NULL) delete address; // Звільнити пам’ять printf («Запровадьте новий адресу: »); gets (s); address = new char[strlen (s)+1]; // Зайняти нову пам’ять strcpy (address, s); // Заповнити полі адреса.

}.

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

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

Що стосується, якщо конструктор об'єкта нового класу заданий звичайним чином, та над викликом цього конструктора будуть викликані конструктори без параметрів для назв об'єктів старого класу. І навпаки, після виклику деструктора для об'єкта нового класу будуть викликані деструкторы вкладених об'єктів старого класса.

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

class man.

{ char name[20]; // Інші елементи класу dat dat1; // Дата народження dat dat2; // Дата надходження працювати public: man (char *, char *, char *); // Конструктори man (char *);

} //——- Конструктор класу man з неявним викликом конструкторів // для dat1 і dat2 без параметрів //——————————————————————————- man: man (char *p).

{.

} //——- Конструктор класу man із явним викликом конструкторів // для dat1 і dat2 з параметрами //————————————————————————————- man: man (char *p, char *p1, char *p2): dat1(p1), dat2(p2).

{ ¦ ¦ ¦

// —- Тіло конструктора —- ¦ ¦ ¦

} ¦ ¦ ¦

Виклик конструктора для ————————— ¦ ¦ вкладеного об'єкта dat1 ¦ ¦

Як параметра передається ——————- ¦ рядок — другий параметр виклику ¦ конструктора для класу man Виклик конструктора для вкладеного об'єкта dat2.

void main ——— Рядок конструктора man { ¦ man JOHN («John », «8−9-1958 », «15−1-1987 »); } ¦ L——— Рядок передается.

Рядок передається конструктору об'єкта конструктору об'єкта dat2 в об'єкті man dat1 в об'єкті man.

6.2 Похідні классы.

——————————;

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

Старий клас у своїй називається базовим класом (БК), новий — похідним класом (ПК).

Синтаксис визначення похідного класу має вид:

class: ,…

{ визначення приватній та публічною частини похідного класса.

}.

Перерахуємо основні властивості базовий і похідного классов:

— об'єкт базового класу визначається похідному класі як неименованный. Це означає, що вона може бути використана вочевидь як звичайний іменований объект;

— елементи даних базового класу входять у об'єкт похідного класу (зазвичай, компілятор розміщає в початку об'єкта похідного класу). Oднако приватна частина базового класу закрита для прямого використання їх у похідному классе;

— элементы-функции базового класу «успадковуються «в похідному класі, тобто виклик функції, певної в базовому класі, для об'єкта похідного класу може бути тлумачать як виклик її для входить у нього об'єкта базового класса;

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

Приклад схеми визначення похідного класса.

class a.

{ public: void f () {} void g () {}.

}.

class b: a ———————————— базовий класс.

{ public: void f () —————————— «f «переопределяется.

{ … a: f (); ——————— явний виклик «f «для БК.

} ——————— «g «наслідується з БК void h () {} ——————— власна функція в ПК.

}.

void main () { a A1; b B1;

B1.f (); ———————- виклик переопределенной b: f ().

B1.g (); ———————- виклик наследуемой a: f () }.

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

Взаємини конструкторів і деструкторів базовий і похідного класів аналогічні вище описанным:

— якщо конструктор похідного класу визначено звичайним чином, то спочатку викликається конструктор базового класу без параметрів, та був конструктор похідного класу. Деструкторы викликаються у порядку — спочатку для похідного, потім для базового;

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

6.3 Права доступу в похідних классах.

——————————————————-;

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

— приватна частина базового класу A завжди входить у приватну частина похідного класу B, та заодно безпосередньо недоступна з элементовфункций класу B. Це відповідає тому факту, що у класі B дозволяється працювати з базовим об'єктом класу A лише дозволеними у п’ятому класі A засобами, тобто елементифункції класу A. Винятком є оголошення всього класу B дружнім у п’ятому класі A;

— за умовчанням, тобто за використанні заголовка вида.

class B: A { }.

публічна частина класу A потрапляє у приватну частина класу B. Це отже, що элементы-функции класу A доступні з элементов-функций класу B, але з виникають ззовні, тобто за зверненні до об'єктів класу B. Тобто для зовнішнього користувача класу B інтерфейс класу A закрывается;

— інакше, при объявлении.

class B: public A { }.

публічна частина класу A потрапляє у публічну частина класу B, і зовнішній користувач під час роботи з об'єктами класу B може застосувати інтерфейси як похідного, і базового классов;

— і, нарешті, у визначенні публічної частини класу B можна явно вказати элементы-функции (і навіть дані) публічної частини базового класу A, які входять у публічну частина класу B, тобто виконати попереднє дію селективно стосовно окремих елементах (у своїй зазначається лише ім'я элемента):

class B: A {.

… public:

… public A: fun;

}.

Перелічені варіанти зображені на схеме:

class A class B —————- ———————— ¦ privat ======================> privat A ¦ +—————+ ¦ (недоступний B)¦ ¦ public ¦ class B: A +———————-+ ¦ ======================> privat B ¦ ¦ ¦ ¦ (доступний B) ¦ ¦ ¦ class B: public A ¦===============¦ ¦ ======================> public B ¦ ¦ ¦ class B: A { … ¦ ¦ ¦ ¦ public A: newadr; ¦ похідного класу L—————- L———————;

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

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

class A.

{ int a1; // Звичайний приватний елемент protected: int a2; // Захищений приватний елемент public:

}.

class B: A // a1, a2 в приватній частини B.

{ void x ();

} void B: x () { a1 = 5; // Помилка: a1 недоступний в B a2 = 3; // a2 доступний в приватній частини B }.

class B: public A // a2 доступний і захищений в приватной.

{ // частини B, неявно має место.

// protected: int a2;

}.

class A class B —————- ———————— ¦ privat ======================> privat A ¦ +—————+ ¦ (недоступний B)¦ ¦ protected¦ class B: A +———————-+ ¦ ======================> privat B ¦ ¦ =============== ¦ (доступний B) ¦ +—————+ class B: public A +———————-+ ¦ public ¦ L======> protected B ==========> ¦ ¦ ¦===============¦ ¦ ¦ ¦ public ¦

6.4 Посилання на об'єкти базовий і похідного классов.

—————————————————————————-;

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

Що стосується базовому і похідному класу можна сказати, що, перетворюючи посилання об'єкт похідного класу до засланні на об'єкт базового класу, ми маємо доступом до вкладеному об'єкту базового класу. Але за такого трактуванні перетворення типу посилання транслятору необхідно враховувати розміщення об'єкта базового класу в похідному, що і робить. Через війну в такому перетворення (привласненні) значення посилання (адресу пам’яті) може бути не рівним вихідному. Через те, що така перехід від об'єкта похідного класу до базового часто трапляється коригується транслятором, це перетворення типу заслання Сі++ може бытьл виконано неявно (інші перетворення типів посилань би мало бути явнями).

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

class A.

{ public: void f1();

};

class B: A.

{ public: void f1(); // Переопределена в классe B void f2(); //.

};

A *pa;

B *pb;

B x;

pa = &x; // Неявний перетворення ссылки.

// на об'єкт класу B в ссылку.

// на об'єкт класу A pa->f1(); // Виклик функції з вложенного.

// об'єкта базового класу A: f1(),.

// хоча вона переопределена.

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

pb = (B*) pa; // Протилежне перетворення — явне pb ->f2(); // Коректно, коли під «pa «был.

// об'єкт класу «B «.

6.5 Принцип объектно-ориентированного программирования.

——————————————————————————-;

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

Під час розробки объектно-ориентированной програми програміст створює похідні класи, які автоматично успадковують все властивості базових, та був переопределяет деякі з функції і додає нові. У принципі так ніщо не перешкоджає будь-якою рівні розробки можливість перейти до традиційному програмування і створювати лінійну програму, використовуючи об'єкти вже існуючих класів. Наслідування технологією объектно-ориентированного програмування «остаточно «передбачає, що прикладна програма є клас найвищого рівня, у її виконання — створення об'єкта цього чи виконання йому деякою функції типу «run » .

Лекція 7. Віртуальні функции.

——————————————;

7.1 Поняття віртуальної функции.

——————————————-;

Досить часто програмісту потрібно створювати структури даних, які включають у собі змінне число об'єктів різних типів. Для уявлення в програмах використовуються списки чи масиви посилань на ці об'єкти. Об'єкти різних класів мають відповідно різні типи посилань, а зберігання ЕВР у масиві чи списку потрібно один тип посилань. Для подолання цього протиріччя всі ці класи об'єктів потрібно зробити похідними від тієї самої базового класу, а під час запису в масив перетворювати посилання об'єкт похідного класу на заслання на об'єкт базового.

p[] A1.

+—-+ -b————;

¦ ——————————->-a———-¦======== b: f ().

+—-+ ¦L————-¦===.

¦ —————— L—————- ¦

+—-+ ¦ C1 ¦

¦ ————— ¦ -з————- ¦

+—-+ ¦ L————>-a———-¦======== c: f ().

¦ ¦L————-¦===¦

¦ L—————- ¦

¦ A1 ¦

L—————>-a———- ===¦==== a: f ().

L———— class a.

{ … void f (); };

class b: public a.

{ … void f (); };

class з: public a.

{ … void f (); };

a A1; b B1; з C1;

a *p[3]; // Масив посилань на об'єкти БК p[0] = &B1; // Посилання на об'єкти БК в p[1] = &C1; // об'єктах ПК p[2] = &A1;

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

p[0]->f (); // Виклик a: f () p[1]->f (); // завжди, хоча f () p[2]->f (); // переопределены.

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

— зберігати в об'єкті базового класу ідентифікатор «навколишнього «його похідного класса;

— у списку чи таблиці зберігати посилання об'єкт базового класса;

— при виклик функції засланні на об'єкт базового класу ідентифікувати тип похідного класу тут і явно викликати йому переопределенную функцию;

— ідентифікатор класу встановлювати під час створення об'єкта, тобто у його конструкторе.

class a.

{ public: int id; // Ідентифікатор класу void f (); void newf (); // Нова функція f () з ідентифікацією ПК.

}.

a:a () // Конструктори объектов.

{ … id = 0;

} b: b ().

{ … id = 1;

} c: c ().

{ … id = 2.

}.

void a: newf ().

{ switch (id).

{ case 0: a: f (); break; case 1: b: f (); break; case 2: c: f (); break;

}.

}.

p[0]->newf (); // Виклик b: f () для B1 p[1]->newf (); // Виклик c: f () для C1 p[2]->newf (); // Виклик a: f () для А1.

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

Отже, якщо перетворення типу «посилання ПК «до типу «посилання БК «відбувається втрата інформацію про об'єкті похідного класу, то, при виклик віртуальної функції відбувається зворотний процес неявного відновлення типу объекта.

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

class A.

{ ———> void (**ftable)(); // Посилання на таблицю адресов.

// віртуальних функцій public: virtual void x (); virtual void y (); virtual void z ();

A ();

~A ();

};

// Таблиця адрес функцій класу А.

———> void (*TableA[])() = { A: x, A: y, A: z };

A:A ().

{ ———> ftable = TableA; // Установка таблиці для класу А.

}.

class B: public A.

{ public: void x (); void z ();

B ();

~B ();

};

// Таблиця адрес функцій класу A.

// у п’ятому класі B.

—> void (*TableB[])() = { B: x, A: y, B: z };

¦ L переопределяется в B.

B:B () L——— наслідується з A.

{ —> ftable = TableB; // Установка таблиці для класу B.

}.

void main () { A* p; // Посилання p базового класу A B nnn; // називає об'єкт производp = &nnn;

// ного класу B реалізація p->z (); —————————> (*(p->ftable[2]))(); }.

p nnn TableB B: z () ——- ————>—B——- ——->————- —->————— ¦ ——— ftable¦—A—-¦ ¦ 0+————+ ¦ ¦ ¦ L——- ¦¦ ——— 1+————+ ¦ ¦ ¦

¦+——-+¦ 2¦ ————- L————- ¦¦ ¦¦ L———-;

7.2 Абстрактні классы.

——————————;

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

class base.

{ public: virtual print () =0; virtual get () =0;

}.

Природно, що визначатиме тіла цих функцій не требуется.

7.3 Множинне запозичення ро-сійських та віртуальні функции.

—————————————————————————;

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

class d: public a, public b, public з { };

d D1; pd = &D1; // #define db sizeof (a) pa = pd; // #define dc sizeof (a)+sizeof (b) pb = pd; // pb = (char*)pd + db pc = pd; // pc = (char*)pd + dc.

D1 pd ——————————>-d————- pa ——————————->-a———-¦T T.

¦¦ ¦¦¦ ¦ db = sizeof (a).

¦L————-¦¦ + pb ——————————->-b———-¦¦ dc = sizeof (a) + sizeof (b).

¦L————-¦¦ pc ——————————->-c———-¦+.

¦L————-¦

¦ ¦

L————-;

Перетворення посилання об'єкт похідного класу до засланні на об'єкт базового класу вимагає додавання до покажчику поточного об'єкта this відповідного усунення (db, dc), зворотне перетворення — віднімання цього ж усунення. Таке дію виконується компілятором, як у об'єкті похідного класу наслідується функція з другого тощо. базового класу, наприклад щодо в класі «b «функції «f () «і його успадкування у п’ятому класі «d «виклик D1. f () буде реалізований наступним образом:

this = &D1; // Адреса об'єкта похідного класу this = (char*)this + db // Адреса об'єкта класу b у ньому b: f (this); // Виклик функції у п’ятому класі b зі своим.

// объектом.

Розглянемо особливості механізму віртуальних функцій при множині успадкування. По-перше, за кожен базовий клас, у похідному класі створюється своя таблиця віртуальних функцій (в нашому випадку — для «a «в «d », для «b «в «d «й у «з «в «d »). По-друге, якщо функція базового класу переопределена в похідному, то при виклик віртуальної функції потрібно перетворити посилання об'єкт базового класу на заслання на об'єкт похідного, тобто на другому тощо. базових класів вилучити з this відповідне усунення. І тому транслятор включає відповідний код, коригувальний значення this як «латки », передавальної управління командою початку переобумовленої функции.

class a.

{ public: virtual void f (); virtual void g ();

}; class b.

{ public: virtual void h (); virtual void t ();

}; class з: public a, public b.

{ // f (), t () успадковуються public: void g (); // g () переопределяется void h (); // h () переопределяется.

}.

a A1; b B1; з C1; pa = &A1; pb = &B1;

pa->f (); // Виклик a: f () pb->h (); // Виклик b: h ().

pa = &C1; pb = &C1;

pa->f (); // Виклик a: f () pa->g (); // Виклик c: g () pb->h (); // Виклик c: h () pb->t (); // Виклик b: t ().

Таблиці віртуальних функцій для даного прикладу мають вид:

A1.

— a—— Таблиця ВФ для «a «.

¦ ——————>————.

+——-+ ¦a:f () ¦

L——— +———-+.

¦a:g () ¦

L———- B1.

— b—— Таблиця ВФ для «b «.

¦ ——————>————.

+——-+ ¦b:h () ¦

L——— +———-+.

¦b:t () ¦

L———- C1.

T —з——- Таблиця ВФ для «a «в «з «.

¦ ¦—a—-¦ ———— db ¦ ¦¦ —————->¦a:f () ¦

¦ ¦L———¦ +———-+.

+ ¦—b—-¦ ¦c:g () ¦

¦¦ ———- L———- ¦L———¦ ¦ Таблиця ВФ для «b «в «з «.

¦ ¦ ¦

¦ ¦ L—->———— «Латка «для c: h ().

L———— ¦ xxx ()——->—xxx ()————————.

+———-+ ¦ this=(char*)this — db¦

¦b:t () ¦ ¦ goto c: h ¦

L———— L——————————-;

Іншим варіантом розв’язання проблеми є зберігання необхідних зсувів лише у таблицях віртуальних функций.

7.4. Віртуальні базові классы.

——————————————-;

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

class base {} class a: public base {} class b: public base {} class з: a, b {}.

У класі «з «присутні два об'єкта класу base. Щоб не допустити такого дублювання об'єкт базового класу може бути оголошено виртуальным.

class a: virtual public base {} class b: virtual public base {} class з: public a, public b {}.

a A1; b B1; з C1;

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

A1 B1 C1.

—a——— —b——- —c———————;

¦ ——— ¦ ——— ¦ —a———- ¦

+————+ ¦ +———-+ ¦ ¦ ¦ ———- ¦

¦ ¦ ¦ ¦ ¦ ¦ ¦ +————-+ ¦ ¦

¦-base—————-0 integer.

+————+ +————+. real.

+————+ +————+j —dat———- base** ¦ ———————>-base——-¦

+————+ ¦L—————¦ base* ¦ ¦

L—————- base head Рядок заголовка БД — S0 ¦———————>————-0 -string—- L— ¦ —————————>-base—-¦ base** +————+ —————>L————¦

¦ ——————— L————- +———- -+ ¦ ¦ D0.

¦ ————- ¦ -dat———.

+————+ L—->-base—-¦ base* ¦L————¦

L————-//——————————— ————————————————// Меню класів об'єктів (типів шпальт) string S0; dat D0; time T0; integer I0;

base *TYPE[] = {.

(base*) &S0;

(base*) &D0;

(base*) &T0;

(base*) &I0;

}; //——————————————————————————-// Створення структури БД.

#define MAXCOL 30 #define MAXREC 1000.

table:table () { int i, j, n; char ss[80]; names = new char*[MAXCOL]; // Таблиця адрес імен шпальт head = new base*[MAXCOL]; // Таблиця посилань на об'єкти for (nc=0; nc.

Показати весь текст
Заповнити форму поточною роботою