рефераты конспекты курсовые дипломные лекции шпоры

Реферат Курсовая Конспект

Конспект лекцій СИСТЕМНЕ ПРОГРАМУВАННЯ

Конспект лекцій СИСТЕМНЕ ПРОГРАМУВАННЯ - Конспект, раздел Образование, Міністерство Освіти І Науки України Східноукраїнський Національний Університе...

МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ СХІДНОУКРАЇНСЬКИЙ НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ ІМЕНІ ВОЛОДИМИРА ДАЛЯ

 

Конспект лекцій

по дисциплiні

«СИСТЕМНЕ ПРОГРАМУВАННЯ»

 

Затверджено

на засіданні кафедри

компютерной інженерії,

протокол № від ______

 

Укладач Є. В. Щербаков

 

 

ВСТУП

В останні десять років відбулися кардинальні зміни в мовних засобах системного програмування. Якщо до цього десятиліття мови типу Асемблера широко використовувалися при розробці операційних систем, компіляторів і пакетів програм, то з середини 90-х роках стали домінувати операційні системи Windows, Unix і Linux, основними| мовами системного програмування яких є мова C та її об’єктно – орієнтоване розширення - мова C++. Більше того, мова програмування C і виникла на початку 70-років минулого сторіччя як мова програмування операційної системи Unix. Слід також відмітити, що інтерфейс прикладного програмування (Application Programming Interface – API) для операційних систем Windows її користувачам надається фірмою Microsoft тільки в формі набору API-функцій на мові програмування C.

Машинно – орієнтована мова Асемблер в даний час використовується тільки для оптимізації функціонування невеликих критичних за часом виконання або специфічних для різних мікропроцесорів ділянок операційних систем і системних програм, таких як BIOS, залежних від пристроїв частин драйверів і т. ін. Можливо, найбільш серйозне застосування Асемблер знаходить при ретрансляції системних програм. Зокрема, операційні системи Windows в аварійних випадках автоматично виводять на Асемблері ретрансльовані тексти ділянок коду, на яких відбувся непоправний збій або зафіксована програмна помилка.

Практично всі основні| засоби, стилі і методи системного і прикладного програмування втілені в мові C++ [1], розробленій Бьєрном Страуструпом в 1983 році.

Мова C++ є мовою системного програмування, основні| характеристики якої є наступними:

- є розвитком (надмножиною) процедурно - орієнтованої мови C;

- підтримує абстракцію даних за допомогою класів;

- підтримує об'єктно-орієнтоване програмування;

- підтримує узагальнене програмування;

- забезпечує використання макросів у нестандартних ситуаціях;

- дає можливість робити асемблерні вставки в програмах.

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

 

1 БАЗОВІ ЕЛЕМЕНТИ МОВИ СИСТЕМНОГО ПРОГРАМУВАННЯ

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

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

Нижче наводиться приклад закінченої програми:

 

#include <iostream>

using namespace std;

void show_title()

{

cout << "Заголовок: Мовні засоби системного програмування." <<’n’;

}

void show_lesson()

{

cout << "Розділ 1: Базові елементи мови." <<’n’;

}

void main()

{

show_title();

show_lesson();

}

 

Коли програма починає виконання з функції main(), вона спочатку викликає функцію show_title(), яка виводить повідомлення на екран, скориставшись стандартним потоком виводу cout, який був підключений до програми її першою директивою #include <iostream>. Після завершення функції show_title() програма викликає функцію show_lesson(), що також виводить повідомлення на екран. Після завершення функції show_lesson() програма завершується, оскільки в головній функції програми main() більше немає інструкцій.

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

1.1 Функції

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

Перш ніж функціяможе бути викликана на виконання, вона має бути визначена.

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

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

Наприклад, функціядобування квадратного кореня виглядає таким чином:

 

double sqrt(double arg)

{

// інструкції, що обчислюють квадратний корінь

}

 

Використання цієї функції:

 

void f()

{

double root2 =2*sqrt(2.0);

}

 

Звичайно функціязавершується виконанням інструкції return e, де е – вираз, значення якого повертається як результат роботи функції. Тип виразу повинен відповідати типу результату функції.

Якщо як тип результату функції задано ключове слово void, це означає, що функціянічого не повертає і інструкція return не повинна використовуватися.

1.2 Коментарі

У мові C++ коментарі в програму можна включати двома різними способами.

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

Другим способом, яким можна включати коментарі в програму на C++, є однорядковий коментар. Однорядковий коментар починається з двох символів косої риски (//) і закінчується кінцем рядка. Іншого символу, крім фізичного кінця рядка, в однорядковому коментарі використовувати не потрібно.

Звичайно в програмах на C++ перший спосіб оформлення використовується для багаторядкових коментарів, а другий – для коротких зауважень, наприклад:

 

/*

Це багаторядковий коментар.

Дана програма визначає

парність цілого.

*/

#include <iostream>

using namespace std;

int main()

{

int num; // це однорядковий коментар C++

// вводу числа

cout << "Введіть число, що перевіряється: ";

cin >> num;

// перевірка на парність

if((num%2)==0) cout << "Число парне. n";

else cout << "Число непарне. n";

return 0;

}

 

Однорядкові коментарі можна вкладати в багаторядкові:

 

/*

Це багаторядковий коментар,

всередину якого // вкладений однорядковий коментар.

Закінчення багаторядкового коментарю.

*/

1.3 Змінні і типи даних

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

Перед використанням в обчисленнях будь-яка змінна має бути оголошена. Основна| форма оголошення наступна:

 

type name;

 

Тут type задає тип змінної (об'єкту), а name – ім'я цієї змінної. Наприклад, інструкція:

 

int cm;

 

оголошує змінну з ім'ям cm, а також задає тип цієї змінної int - ціле число.

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

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

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

 

int а = 27; // ініціалізація змінної при оголошенні

cm = а; // присвоювання значення змінній

 

У C++ є багато вбудованих типів, основні| з яких перелічені нижче:

 

bool - логічний тип, змінна може містити значення true (істина) або false (хибність);

char - символьний тип, змінна може містити один алфавітно-цифровий або спеціальний символ;

int - тип цілих чисел;

float - тип чисел з плаваючою крапкою;

double - тип чисел з плаваючою крапкою подвоєної точності.

 

До оголошень цілих типів зліва можуть приписуватися один або два модифікатори: signed – значення зі знаком, unsigned – значення без знаку, short – коротке, long – довге ціле; при цьому саме оголошення int може опускатися. Модифікатор long також може використовуватися з числами з плаваючою крапкою типу double.

Логічні, символьні і цілі типи всі разом узагальнено називаються цілими (строго кажучи, інтегральними) типами. Цілі типи спільно з типами з плаваючою крапкою називаються арифметичними типами.

1.4 Вказівники і масиви

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

Наприклад:

 

char *cp; // вказівник на символ

int *iq; // вказівник на ціле

int (*fp)(int); // вказівник на функцію, аргументом

// яким є ціле число і яка

// повертає ціле число як результат

 

Перед використанням в програмі вказівник повинен містити адресу об'єкту потрібного типу. Перед присвоюванням вказівнику адреса змінної, елементу масиву, функції або іншого об'єкту може бути одержана за допомогою оператора узяття адреси (&), наприклад:

 

q = &cm; // вказівник містить адресу змінної inch

fp = &max; // вказівник містить адресу функції max

 

Оператор розіменування вказівника (*) використовується для доступу до значення об'єкту, що адресується вказівником, наприклад:

 

int i = *q; // змінна i містить 27 - значення cm

 

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

 

char v[10]; // масив з 10 символів

int ia[5] = {10, 20, 30, 40, 50}; // масив цілих із значеннями,

// переліченими в списку після

// знаку "="

 

Для звернення до значення елементу масиву задається ім'я масиву і індекс потрібного елементу в квадратних дужках, наприклад: v[5], а[0].

У всіх масивів нижня межа – 0, тобто масив v[10] – це елементи: v[0], v[1]…,v[9].

Виходячи зі сказаного, визначення функції копіювання 10-ти елементів з одного масиву в іншій виглядатиме таким чином:

 

void copy_function()

{

int v1[10];

int v2[10];

for (int i = 0; i<10; ++i)

v1[i]= v2[i];

}

1.5 Літерали

Літералом називається послідовність символів, яка інтерпретується як безпосередньо задане значення одного з базових типів. У мові C++ літерал є одним з двох різновидів констант; другим різновидом є типовані |константи, які оголошуються як звичайні змінні, але з модифікатором const.

1.5.1 Логічні літерали

Логічні величини можуть приймати два значення: true – істина або false – хибність. У логічних виразах цілі значення приводяться до логічних: нульове ціле значення завжди трактується як false, ненульове – як true.

1.5.2 Символьні літерали

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

 

'f', 'a', 'я'|, '1'

 

Спеціальні та контролюючі символи мають в мові С++ стандартні імена, які починаються з символу зворотної косої риски () як escape – символу. Всі ці символи перелічені в табл. 1.1.

 

Таблиця 1.1

Спеціальні і контролюючі символи

Назва символу Позначення ASCII Запис на C++
Переведення рядка NL(LF) n
Горизонтальна табуляція HT t
Вертикальна табуляція VT v
Повернення назад BS b
Повернення каретки CR r
Переведення сторінки FF f
Сигнал BEL а
Зворотна коса риска
Знак запитання ? ?
Одиночні лапки ' '
Подвійні лапки " "
Нульовий символ NUL
Вісімкове число ooo ooo
Шіснадцяткове число hhh xhhh

 

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

1.5.3 Цілі літерали

Цілі літерали можуть представлятися десятковими, вісімковими або шіснадцятковими числами.

Десяткове число окрім необов’язкового знака числа містить цифри 0 – 9 і завжди починається з ненульової цифри, наприклад: 956.

Вісімкове число завжди починається з цифри 0 і може містити тільки цифри 0 – 7, наприклад: 076 (62 в десятковому представленні).

Шіснадцяткове число завжди має префікс 0x або 0X і складається з комбінації цифр 0 – 9 і букв а – f або A – F, наприклад: 0x12 (18 в десятковому зображенні), 0XA3 (163 в десятковому зображенні).

Довгий цілий літерал явно визначається латинською буквою l або L, що стоїть після десяткового, вісімкового або шіснадцяткового числа, наприклад: 188l, 036L, 0x1B9l.

1.5.4 Літерали з плаваючою крапкою

За умовчанням літерали з плаваючою крапкою є числами типу double, наприклад:

 

345. (345 десяткове)

3.14159 (3.14159 десяткове)

.123e3 (123 десяткове)

4037E-5 (.04037 десяткове)

 

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

Якщо потрібен літерал типу float, його явно можна задати за допомогою суфікса f або F, наприклад:

 

345.0F

3.14159f

.123e3F

1.5.5 Рядкові літерали

Рядкові літерали представляються послідовністю символів, взятою в подвійні лапки. Наприклад:

 

"This is а character string"

"Це рядок символів."

"123456789"

 

Якщо рядковий літерал не вміщається в один рядок редактора інструментальної системи (наприклад, MS Visual Studio 7.1), він записується на декількох рядках, кожен з яких береться в подвійні лапки.

Хоча зовні рядковий літерал – це послідовність символів, взятих у лапки, при розміщенні його в пам'яті в кінець послідовності додається ще один службовий символ з нульовим кодом. Рядковий літерал має тип char[], тобто він розглядається як масив символів.

Вирази

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

 

x++ b = 10 x = (y*z)/w у = 2*sqrt(6.0)

 

Потрібно відмітити, що вираз, за яким слідує крапка з комою (;), є однією з інструкцій мови C++, які детально розглядатимуться в наступному розділі.

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

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

 

int inuml = 3;

int ianswer;

ianswer = (inuml = 4) + (inuml *2);

 

Інтуїтивно зрозуміло, що для оператора присвоювання (=) треба спочатку оцінити вираз в правій частині, а потім результат потрібно помістити за адресою, одержаною в результаті обчислення (якщо таке треба було робити) лівої частини. При виконанні оператора "+" також важливо, лівий або правий підвираз обчислювати першим. Якщо лівий, а потім правий – результат буде рівним 12 (4 + 4*2); якщо правий, а потім лівий – результат буде 10 (4 + 3*2). Напрям оцінки операндів перед виконанням оператора називається його асоціативністю; як правило, вона однозначно визначена для кожного оператора мовою і компілятором цієї мови.

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

 

void some_function ()

{

// оголошення змінних і присвоювання початкових значень

double d = 2.2;

int i = 7;

// обчислення

d = d + i;

i = d * i;

}

 

Оператори

Всі оператори мови C++ у порядку зменшення пріоритетів їх виконання при обчисленні виразів приведені в табл. 1.2.

У таблиці оператори, що мають однакові пріоритети виконання, згруповані разом. Групи відокремлені одна від одної суцільними горизонтальними лініями; усередині групи оператори розділені пунктирними лініями. Групи розташовуються у порядку зменшення пріоритетів операторів, що містяться в них.

Напрям оцінки операндів для кожного оператора приведений в колонці 3. В колонці 4 таблиці приводяться варіанти використання кожного оператора. У цій колонці таблиці, як і в усій книзі, використані наступні позначення:

- name – довільне ім'я;

- class_name - ім'я класу;

- namespase_name - ім'я простору імен;

- qualified_name – кваліфіковане ім'я (послідовність простих імен, розділених крапкою);

- member – ім'я члена класу;

- object – вираз, результатом оцінки якого є об'єкт якогось класу;

- pointer - вираз, результатом оцінки якого є вказівник на об'єкт;

- pointer_to_member – вказівник на член класу;

- expr – деякий вираз, по типу відповідний даному оператору;

- expr_list – список виразів, розділених комою;

- lvalue – вираз, який після оцінки задає об'єкт, значення якого може бути змінено;

- type – ім'я типу.

 

Таблиця 1.2

Оператори мови C++, приведені по групам в порядку зменшення пріоритетів їх виконання

Оператор Виконувана операція Асоціативність Використання
() Круглі дужки, підвираз Зліва направо (expr)
:: Визначення області видимості для класів Справа наліво class_name::member
:: Визначення області видимості для простору імен Справа наліво namespace_name::member
:: Глобальне ім'я Справа наліво ::name
:: Глобальне кваліфіковане ім'я Справа наліво ::qualified_name
. Вибір члена структури або класу по імені об'єкту Зліва направо object.member
-> Вибір члена структури або класу через вказівник на об'єкт Зліва направо pointer->member
[] Доступ до елементу масиву по індексу Зліва направо pointer[expr]
() Виклик функції Зліва направо expr(expr_list)
() Ініціалізація члена класу Зліва направо member(expr)
++ Постфіксне збільшення Справа наліво lvalue++
-- Постфіксне зменшення Справа наліво lvalue--
typeid Ідентифікація типу. Повертає посилання на структуру type_info, визначену в бібліотеці <typeinfo> Справа наліво typeid(type)
typeid Ідентифікація типу виразу під час виконання. Повертає посилання на структуру type_info, визначену в бібліотеці <typeinfo> Справа наліво typeid(expr)
dynamic_cast Приведення типів з перевіркою під час виконання Справа наліво dynamic_cast<type>(expr)
static_cast Приведення споріднених типів (наприклад, тип з плаваючою крапкою в цілий тип) з перевіркою під час компіляції Справа наліво static_cast<type>(expr)
reinterpret_cast Приведення між не зв'язаними типами (наприклад, цілих у вказівники, одних вказівників в інші) без перевірки Справа наліво reinterpret_cast<type>(expr)
const_cast Константне приведення. Анулює дію модифікатора const Справа наліво const_cast<type>(expr)
sizeof Отримання розміру об'єкту Справа наліво sizeof expr
sizeof Отримання розміру типу Справа наліво sizeof(type)
++ Префіксне збільшення Справа наліво ++lvalue
-- Префіксне зменшення Справа наліво --lvalue
~ Побітове заперечення НЕ Справа наліво ~lvalue
! Логічне заперечення НЕ (not) Справа наліво !expr
- Унарний мінус Справа наліво -expr
+ Унарний плюс Справа наліво +expr
& Отримання адреси Справа наліво &lvalue
* Розіменування – непряме звернення до об'єкту через вказівник Справа наліво *expr
new Динамічне виділення пам'яті і створення об'єкту заданого типу Справа наліво new type
new[] Динамічне виділення пам'яті і створення масиву об'єктів заданого типу Справа наліво new type[expr]
delete Динамічне знищення об'єкту із звільненням пам'яті Справа наліво delete pointer
delete[] Динамічне знищення масиву об'єктів із звільненням пам'яті Справа наліво delete[] pointer
() Приведення виразу до заданого типу Справа наліво (type)expr
.* Вибір по імені об'єкту змінної або функції - члена класу непрямо через вказівник Зліва направо object.*pointer_to_member
->* Вибір через вказівник на об'єкт змінної або функції – члена класу непрямо через вказівник Зліва направо pointer->*pointer_ to_member
* Множення Зліва направо expr * expr
/ Ділення Зліва направо expr / expr
% Залишок від ділення Зліва направо expr % expr
+ Складання Зліва направо expr + expr
- Віднімання Зліва направо expr - expr
<< Переміщення вліво Зліва направо expr << expr
>> Переміщення вправо Зліва направо expr >> expr
<< Перевантажений оператор виводу в потік вводу - виводу Зліва направо cout << expr
>> Перевантажений оператор вводу з потоку вводу - виводу Зліва направо cin >> lvalue
< Менше Зліва направо expr < expr
<= Менше або рівно Зліва направо expr <= expr
> Більше Зліва направо expr > expr
>= Більше або рівно Зліва направо expr >= expr
== Рівно Зліва направо expr == expr
!= Не рівно Зліва направо expr != expr
& Побітове І (AND) Зліва направо expr & expr
^ Побітове АБО (OR) без виключень Зліва направо expr ^ expr
| Побітове АБО Зліва направо expr | expr
&& Логічне І Зліва направо expr && expr
|| Логічне АБО Зліва направо expr || expr
= Просте присвоювання Справа наліво lvalue = expr
*= Множення з присвоюванням Справа наліво lvalue *= expr
/= Ділення з присвоюванням Справа наліво lvalue /= expr
%= Залишок від ділення з присвоюванням Справа наліво lvalue %= expr
+= Складання з присвоюванням Справа наліво lvalue += expr
-= Віднімання з присвоюванням Справа наліво lvalue -= expr
<<= Переміщення вліво з присвоюванням Справа наліво lvalue <<= expr
>>= Переміщення вправо з присвоюванням Справа наліво lvalue >>= expr
&= Побітове І з присвоюванням Справа наліво lvalue &= expr
|= Побітове АБО з присвоюванням Справа наліво lvalue |= expr
^= Побітове АБО без виключень з присвоюванням Справа наліво lvalue ^= expr
?: Умовний вираз Справа наліво expr?expr:expr
, Кома (послідовність) Зліва направо expr, expr

 

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

1.7.1 Оператори вводу – виводу

Оператори вводу (>>) і виводу (<<) не є вбудованими в мову C++, а забезпечуються стандартною бібліотекою за допомогою потоків вводу – виводу. При запуску програми на виконання автоматично відкриваються три стандартні потоки мови C++:

cin – стандартний потік вводу (з клавіатури);

cout – стандартний потік виводу (на дисплей);

cerr – стандартний потік для видачі повідомлень про помилки (на дисплей).

Для виконання операторів потокового вводу – виводу в програму повинна бути включена інструкція препроцесора:

 

#include <iostream>

 

Нижче будуть коротко описані потокові операції, що виконуються операторами вводу і виводу мови C++. Детальна інформація про систему вводу – виводу мови C, побудовану на основі бібліотечних функцій, міститься в кінці розділу 3. Детальному опису системи вводу – виводу мови C++ присвячений окремий, дев'ятий, розділ навчального посібника.

Оператор вводу

Стандартним потоком для оператора вводу (>>) є потік cin, який звичайно являє собою клавіатуру комп'ютера. Ім'я цього потоку в виразах розміщується зліва від оператора вводу. Тип операнду праворуч від оператора вводу визначає спосіб інтерпретації символів, що вводяться з клавіатури, а сам операнд задає те, куди записуватимуться значення з вхідного потоку.

Наприклад:

 

void f()

{

int i;

double d;

char name[20];

cin >> i; // зчитування в i цілого числа

cin >> d; // зчитування в d числа з плаваючою крапкою

// подвійної точності

cin >> name; // зчитування в масив name рядка символів

}

 

Приведена функціяпрочитує набране на клавіатурі ціле число (наприклад, 4321) і поміщає його в змінну, потім вводить в змінну d число з плаваючою крапкою (наприклад, 12.34E5), після чого прочитує набраний на клавіатурі рядок символів (наприклад, ім'я “Валентин”).

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

 

cin >> i >> d >> name;

 

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

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

Оператор виводу

Стандартним потоком для оператора виводу (<<) є потік cout, що є, як правило, дисплеєм комп'ютера. Замість потоку cout для виводу повідомлень про помилки може використовуватися потік cerr. Ім'я потоку виводу розміщується зліва від оператора виводу, а праворуч від оператора поміщається вираз, значення якого потрібно помістити в потік. Перед видачею значення виразу, що виводиться, перетворюється в послідовність символів.

Наприклад:

 

void f()

{

cout << 10;

}

 

Ця функціяпомістить символ 1, а потім символ 0 в стандартний потік виводу, тобто на екран в рядок, на який указує курсор. Те ж саме зробить і наступна функція:

 

void g()

{

int i = 10;

cout << i;

}

 

Можна виводити значення різних типів, наприклад:

 

void h(int i)

{

cout << “Значення i рівне “; // вивід тексту

cout << i; // вивід цілого

cout << ‘n’; // вивід одного символу

}

 

Остання інструкція функції h() поміщає у вихідний потік один символ, в даному прикладі це є перехід на новий рядок. При виводі окремих символів кожен символ повинен братися в одиночні лапки на відміну від виводу рядків символів, коли рядок, що виводиться, береться в подвійні лапки.

Так само, як і для операторів вводу, результат оператора виводу можна використовувати для подальшого виводу. Тому приведену вище функцію можна переписати красивіше:

 

void h2(int i)

{

cout << “Значення i рівне “ << i << ‘n’;

}

 

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

 

#include <iostream>

using namespace std;

int main()

{

char name[20];

cout << “Будь ласка, введіть Ваше ім'я.n”

cin >> name;

cout << “Привіт ” << name << “!n”

}

1.8 Інструкції

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

Всі інструкції мови C++, окрім блоків інструкцій, закінчуються крапкою з комою (;).

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

У табл. 1.4 приведений перелік інструкцій мови C++:

 

Таблиця 1.4

Перелік інструкцій мови C++

Тип інструкції Синтаксис інструкції, примітки
Порожня інструкція ;
Блок {послідовність інструкцій}
Оголошення Оголошення змінних, структур, класів, функцій
Інструкція-вираз вираз;
Розгалуження по умові if if (умова) інструкція
Розгалуження по умові if / else if (умова) інструкція 1 else інструкція 2
Інструкція вибору switch switch (вибираючий вираз) { оголошення case константа 1: послідовність інструкцій 1 case константа 2: послідовність інструкцій 2 . case константа N: послідовність інструкцій N default: послідовність інструкцій }
Цикл while while (умова) інструкція
Цикл do / while do інструкція while (умова)
Цикл for for (ініціалізуючий вираз; умова; модифікуючий вираз) інструкція
Інструкція припинення break break;
Інструкція припинення з продовженням continue continue;
Інструкція повернення return return вираз;
Безумовний перехід goto goto ідентифікатор; . ідентифікатор: інструкція
Контроль і обробка виключень try / catch try { послідовність інструкцій, генеруючих виключення } catch (тип1 arg) { послідовність інструкцій, обробляючих виключення типу1 } catch (тип2 arg) { послідовність інструкцій, обробляючих виключення типу2 } … catch (типN arg) { послідовність інструкцій, обробляючих виключення типу N }
Генерація виключення throw throw expr; //тип виразу ідентифікує // виключення, що генерується // цією інструкцією

 

Інструкції, включаючи інструкції, що змінюють порядок виконання інструкцій програми (if, if / else, switch, while, do / while, for, throw), можуть вкладатися одна в одну, що дає можливість будувати вельми складні з багатьма шляхами виконання програми.

Ідентифікатор з подальшою двокрапкою (мітка) може стояти перед будь-якою інструкцією програми, що необхідно, якщо на цю інструкцію передається управління інструкцією goto. Наприклад:

 

ABCD2: x = 3.5;

РОБОТА З ДАНИМИ

Цей розділ містить поглиблену інформацію по роботі з глобальними, статичними і локальними даними в програмах, зокрема правила роботи з масивами,…

Оголошення

  char ch; int count = 1;

Синтаксис оголошення

  char *kings[] = { “Антигін”, “Сельовк”, “Птоломей”};  

Посилання

Посилання головним чином використовуються для задання аргументів і значень, що повертаються функціями. Запис:   type &reference_name

Масиви

Для заданого типу type оголошення:

 

type array_name[size];

 

формує в пам'яті масив з size елементів типу type. Елементи індексуються від 0 до size-1. Наприклад:

 

float v[3]; // масив з трьох чисел з плаваючою крапкою:

// v[0], v[1], v[2]

int а[2][5]; // два масиви, з п'яти цілих кожен

char* vpc[31]; // масив з 32–х вказівників на символи

// vpc[0], vpc[1], …, vpc[31]

 

Для звернення до значення елементу масиву задається ім'я масиву і індекс елементу масиву index в квадратних дужках:

 

array_name[index]

 

Можна таким чином записати цикл, в якому друкуються цілі значення малих літер:

 

extern "C" int strlen(const char*); // з <string.h>

char alpha[] = "abcdefghijklmnopqrstuvwxyz";

main()

{

int sz = strlen(alpha);

for (int i = 0; i<sz; i++)

{

char ch = alpha[i];

cout << '''<< ch << '''

<< " = " << int(ch)

<< " = 0" << oct(ch)

<< " = 0x" << hex(ch)<< 'n';

}

}

 

Тут функції oct() і hex() повертають значення свого аргументу цілого типу у вісімковому і шіснадцятковому зображенні, відповідно. Обидві функції описані в бібліотечному файлі <iostream>. Для підрахунку числа символів в alpha використовується функціяstrlen() з бібліотечного файлу <string>; але замість неї можна було б використати розмір масиву alpha. Результат роботи програми буде таким:

 

'a' = 97 = 0141 = 0x61

'b' = 98 = 0142 = 0x62

'c' = 99 = 0143 = 0x63

...

 

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

 

char v[9];

v = "а string"; // помилка

 

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

 

int v[5][2]; // правильно

int badv[4, 1]; // помилка

2.4.1 Ініціалізація масивів

Рядки придатні тільки для ініціалізації символьних масивів; для інших типів масивів доводиться використовувати складніший запис, в якому початкове значення масиву присвоюється за допомогою списку значень. Наприклад:

 

int v1[4] = {1, 2, 3, 4};

int v2[4] = {'a', 'b', 'c', 'd'};

 

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

 

char v3[] = {1, 2, 3, 4};

char v4[] = {'a', 'b', 'c', 'd'};

 

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

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

 

char v[2][5]= {

{'a', 'b', 'c', 'd', 'e'}

{'0', '1', '2', '3', '4'}

};

 

Цей масив може бути використаний таким чином:

 

main()

{

for (int i = 0; i<2; i++)

{

for (int j = 0; j<5; j++)

cout << "v[" << i << "][" << j

<< "] =" << v[i][j] << " ";

cout << 'n';

}

}

 

В результаті роботи програми одержимо на екрані:

 

v[0][0]=a v[0][1]=b v[0][2]=c v[0][3]=d v[0][4]=e

v[1][0]=0 v[1][1]=1 v[1][2]=2 v[1][3]=3 v[1][4]=4

 

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

 

int v5[8]= {1, 2, 3, 4};

 

рівнозначно

 

int v5[8]= {1, 2, 3, 4, 0, 0, 0, 0};

2.4.2 Вказівники й масиви

Вказівники й масиви в мові C++ тісно зв'язані. Ім'я масиву можна використовувати як вказівник на його перший елемент. Наприклад:

 

int v[] = {1, 2, 3, 4};

int p1 = v; // вказівник на перший елемент масиву

int p2 = &v[0]; // також вказівник на перший елемент масиву

int p3 = &v[4]; // вказівник на елемент, наступний за останнім

// елементом масиву

 

Графічно цей приклад відображений на мал. 2.2.

 

 
 

Малюнок 2.2 Зв'язок між масивом і вказівниками

 

У мові гарантується осмисленість значення вказівника на елемент, наступний за останнім елементом масиву.

Приклад програми роботи з масивом alpha з застосуванням вказівника можна переписати так:

 

int main()

{

char alpha[] = "abcdefghijklmnopqrstuvwxyz";

char *p = alpha;

char ch;

while (ch = *p++)

cout << '''<< ch << '''

<< " = " << int(ch)

<< " = 0" << oct(ch)

<< " = 0x" << hex(ch)<< 'n';

}

 

Можна також оголосити p таким чином:

 

char *p = &alpha[0];

 

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

 

void f()

{

extern "C" int strlen(const char*); // з файлу <string.h>

char v[] = "Annemarie";

char *p = v;

strlen(p);

strlen(v);

}

2.4.3 Доступ до елементів масиву

Доступ до елементів масиву може проводитися або за допомогою вказівника на масив та індексу елементу, або через вказівник на елемент масиву. Розглянемо приклад функції обчислення суми елементів масиву з використанням індексу для перебору елементів масиву:

 

int fi(int v[], int N)

{

int sum = 0;

for (int i = 0; i < N; i++) sum += v[i];

return sum;

}

 

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

 

int fp(int v[], int N)

{

int sum = 0;

int *pN = v + N;

for (int *p = v; p != pN ; i++)

sum += v[*p];

return sum;

}

 

Як вже мовилося раніше, префіксний оператор * означає розіменування, тому *p є значенням елементу масиву, на який вказує р.

Результат застосування до вказівників арифметичних операцій +, -, ++ або -- залежить від типу вказуваних об'єктів. Якщо така операція застосовується до вказівника p типу T*, то вважається, що p указує на масив об'єктів типу T. Тоді p+1 позначає наступний елемент цього масиву, а p-1 - попередній елемент. Звідси витікає, що значення p+1 буде на sizeof(T) байтів більше, ніж значення р.

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

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

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

Типи даних користувача

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

Структури мови C

Оголошення структури має наступний формат:   struct struct_name

Поля

Для змінної, що приймає тільки два значення (наприклад: так, ні), здається марнотратством використовувати тип char, хоча об'єкт типу char в мові С++ є найменшим об'єктом, який може незалежно розміщуватися в пам'яті.

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

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

 

struct sreg

{

unsigned enable : 1;

unsigned page : 3;

unsigned : 1; // не використовується

unsigned mode : 2;

unsigned : 4; // не використовується

unsigned access : 1;

unsigned length : 1;

unsigned non_resident : 1;

};

 

Приведена структура описує розряди нульового регістра стану для старого міні – комп'ютера СМ-4 (передбачається, що поля в слові розміщуються зліва направо). Цей приклад показує також інше можливе застосування полів: давати імена тим частинам об'єкту, розміщення яких в розрядах пам’яті визначено ззовні, наприклад, архітектурою комп'ютера.

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

В ядрі операційної системи або в програмі - налагоджувачу приведений раніше тип sreg міг би використовуватися таким чином:

 

sreg *sr0 = (sreg*)0777572;

//...

if (sr0->access) // порушення прав доступу

{

// інструкції аналізу ситуації

sr0->access = 0;

}

 

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

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

2.5.3 Об'єднання

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

Оголошення об'єднання подібно до оголошення структури, але оголошення об'єднання починається з ключового слова union і має наступний формат:

 

union union_name

{

type1 member1_name;

type2 member2_name;

typeN memberN_name;

};

 

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

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

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

 

struct entry

{

char* name;

char type;

char* string_value; // використовується, якщо type == 's'

int int_value; // використовується, якщо type == 'i'

};

 

void print_entry(entry* p)

{

switch(p->type)

{

case 's':

cout << p->string_value;

break;

case 'i':

cout << p->int_value;

break;

default:

cerr << "поле типу помилкове n";

break;

}

}

 

Оскільки змінні string_value та int_value ніколи не можуть використовуватися одночасно, очевидно, що частина пам'яті пропадає даремно. Це можна легко виправити, оголосивши обидві змінні як члени об'єднання, наприклад, так:

 

struct entry {

char* name;

char type;

union

{

char* string_value; // використовується, якщо type == 's'

int int_value; // використовується, якщо type == 'i'

};

};

 

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

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

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

Іноді об'єднання використовують для приведень типів змінних (в основному на це йдуть програмісти, звиклі до мов, в яких немає засобів приведення типів, і в результаті доводиться обдурювати компілятор). Приведемо приклад такого "приведення" int в int*, яке досягається просто іншою інтерпретацією вмісту розрядів:

 

struct fudge

{

union

{

int i;

int* p;

};

};

 

fudge а;

a.i = 4095;

int* p = а.p; // не дуже коректне використання

 

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

Іноді об'єднання використовують спеціально, щоб уникнути приведення типів. Наприклад, можна використати структурну змінну fudge, щоб дізнатися, як представляється вказівник 0:

 

fudge.p = 0;

int i = fudge.i; // i необов'язково повинно бути 0

 

Об'єднанню можна дати ім'я, тобто можна зробити його повноправним типом. Наприклад, fudge можна оголосити так:

 

union fudge {

int i;

int* p;

};

 

і використовувати його точно так, як і раніше, коректно або некоректно.

Оголошення typedef

  typedef існуючий_тип синонім  

Оператор dynamic_cast

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

Синтаксис використання оператора наступний:

 

dynamic_cast<type>(expr)

 

В| кутових дужках замість type задається цільовий тип, до якого потрібно перетворити значення виразу expr, заданого в круглих дужках.

Цільовий тип повинен бути типом вказівника або посилання; результат приведення виразу теж стає вказівником або посиланням. Таким чином, оператор dynamic_cast використовується для приведення типу одного вказівника або посилання до типу іншого вказівника або посилання.

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

Як приклад розглянемо наступну ситуацію. Хай тип Base – це базовий клас, а Derived – це клас, похідний (тобто успадковує, включає всі можливості) від класу Base. Тоді наступний код буде виконаний успішно:

 

Base *bp;

Base bob;

Derived *dp;

Derived dob;

bp = &dob; // вказівник базового класу вказує

// на об'єкт похідного класу

dp = dynamic_cast<Derived *>(bp);

if (!dp)

cout << “Приведення типів пройшло успішно”;

 

Класи, спадкування класів і поліморфізм детально розглядаються пізніше, в розділі 7 даного навчального посібника.

Оператор static_cast

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

 

static_cast<type>(expr)

 

В кутових| дужках замість type задається цільовий тип, до якого потрібно перетворити значення виразу expr, заданого в круглих дужках.

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

Наприклад:

 

int i;

float f;

f = 199.22;

i = static_cast<int>(f);

cout << i;

Оператор reinterpret_cast

Оператор reinterpret_cast призначений для виконання операцій приведення між незв'язаними типами. Синтаксис його використання подібний до попереднього оператора:

 

reinterpret_cast<type>(expr)

 

В кутових| дужках замість type задається цільовий тип, до якого потрібно привести значення виразу expr, заданого в круглих дужках.

Цей тип приведення може використовуватися для виконання приведень вказівника одного типу у вказівник іншого типу, цілого у вказівник, вказівника в ціле та інше.

Наприклад:

 

int i;

char *p = “Це рядок”;

i = reinpret_cast<int>(p);

cout << i;

Оператор const_cast

Оператор const_cast призначений для видалення атрибутів змінної const і volatile. Синтаксис використання подібний до попереднього оператора:

 

const_cast<type>(expr)

 

В кутових| дужках цільовий тип type повинен співпадати з типом виразу expr, заданим в круглих дужках.

Оператор const_cast може перетворити вказівник на об'єкт будь-якого типу до цього ж типу, але| при цьому ліквідувавши дію атрибутів const та / або volatile.

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

Наприклад:

 

void f(const int *p)

{

int *v;

v = const_cast<int *>(p); // приведення типу позбавляє змінну,

// на яку вказує p, статусу

// константи

*v = 100; // через приведений вказівник можна

// змінити об'єкт

3 ФУНКЦІЇ. ПРОЦЕДУРНЕ ПРОГРАМУВАННЯ

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

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

Формально в мові C++ є тільки функції, а підпрограми трактуються як функції, які нічого не повертають, що позначається ключовим словом void перед ім'ям функції.

3.1 Оголошення функцій

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

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

Базовий синтаксис оголошення прототипу функції наступний:

 

type function_name(type1 a1, type2 a2, typeN aN);

 

Тут type – це тип значення, що повертається функцією: void, int, float і т. д. Хоча як імена функцій function_name можуть використовуватися довільні імена, бажано, щоб вони відображали суть оголошуваних функцій. Функція може мати один, декілька (N) або не мати жодного аргументу. Якщо такі є, оголошення аргументів, кожне з яких включає тип і необов'язкове ім'я аргументу, повинні бути перелічені через кому в круглих дужках після імені функції. Якщо аргументи у функції відсутні, порожні дужки () все рівно повинні бути присутніми в оголошенні функції; щоб підкреслити відсутність аргументів, допускається також запис в круглих дужках слова void.

Приклади прототипів функцій

 

double sqrt(double);

elem* next_elem();

char* strcpy(char* to, const char* from);

void exit(int);

 

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

 

double sr2 = sqrt(2);

 

містить правильний виклик функції sqrt() з аргументом з плаваючою крапкою double(2)== 2.0. Контроль і приведення типів фактичних аргументів має в мові С++ важливе значення.

В оголошенні функції можна указувати імена аргументів. Це полегшує читання програми, але компілятор ці імена просто ігнорує.

3.2 Визначення функцій

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

Синтаксис визначення функції наступний:

 

type function_name(type1 a1, type2 a2., typeN aN)

{

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

// інструкції, що реаліують алгоритм обчислення

// результату функції

return (expr); // повернення результату, якщо потрібно

}

 

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

Наприклад:

 

void swap(int*, int*); // оголошення прототипу функції

 

void swap(int* p, int* q) // визначення функції

{

int t = *p;

*p = *q;

*q = *t;

}

 

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

 

void search(table* t, const char* key, const char*)

{

// третій аргумент не використовується

...

}

 

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

3.2.1 Вмонтовані (inline) функції

У мові C++ код, що складає тіло функції, може підставлятися безпосередньо замість кожного її виклику на виконання в програмі, якщо прототип функції заданий з ключовим словом inline. Такі функції називаються вмонтованими або inline – функціями.

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

 

#include <iostream>

using namespace std;

inline long square(int а){return а * а;}

int main()

{

long sum = 0;

for(int i =1; i<=100; i++)

sum += square(i);

cout <<sum;

}

 

При компіляції, виявивши, що функціяsquare() оголошена з ключовим словом inline, компілятор замінить виклик цієї функції її реалізацією (інструкція sum += square(i); буде замінена на інструкцію sum += i * i;).

Компілятор може проігнорувати рекомендацію, задану inline, якщо, наприклад, тіло функції дуже велике або функціяє рекурсивною.

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

 

inline int fac(int i) {return i<2 ? 1 : n*fac(n-1);}

 

3.3 Передача аргументів при виклику функції

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

Аргументи можуть передаватися за значенням або за посиланням.

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

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

Щоб зрозуміти різницю, як приклад розглянемо функцію:

 

void f(int val, int& ref)

{

val++;

ref++;

}

 

При виконанні f() у виразі val++ збільшується на одиницю значення локальної копії першого фактичного аргументу, тоді як в ref++ - сам другий фактичний аргумент збільшується на одиницю. Тому в функції:

 

void g()

{

int i = 1;

int j = 1;

f(i, j);

}

 

збільшиться значення j, але не i, оскільки перший аргумент i переданий за значенням, а другий аргумент j переданий за посиланням.

Посилання на константи часто використовуються як аргументи функцій.

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

 

void incr(int &aa)

{

aa++;

}

 

void f()

{

int x = 1;

incr(x); // x = 2

}

3.3.1 Використання посилань як аргументів

Оскільки передача аргументів має ту ж семантику, що й ініціалізація, в наведеному вище прикладі при виклику функції incr() її аргумент aa стає іншим ім'ям для x.

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

 

int next(int а)

{

return a+1;

}

void inc(int* p)

{

(*p)++;

}

void g()

{

int x = 1;

x = next(x); // x = 2

inc(&x); // x = 3

}

 

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

Як приклад запрограмуємо простий асоціативний масив. Почнемо з визначення структури pair:

 

struct pair

{

char* name; // рядок

int val; // ціле

};

 

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

 

Масиви як аргументи

  int strlen(const char*); void f()

Аргументи за умовчанням

Розглянемо як приклад функцію друку цілого числа. Цілком розумно застосувати як необов'язковий аргумент основу системи числення числа, що…   void printb(int value, int base =10); // основа за умовчанням рівна 10

Макрос WINAPI

Для забезпечення сумісності з наявними програмами заголовний файл WINDOWS.H містить макрос WINAPI, який потрібно задавати там, де раніше потрібно…   3.10 Функції вводу – виводу мови C

ПРОГРАМУВАННЯ ВИКЛЮЧЕНЬ

Події, що приводять до виключень, можуть породжуватися помилками, що виявляються апаратно, наприклад, діленням на нуль або зверненням до пам'яті за…

Загальна схема обробки виключень

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

4.1.1 Інструкція throw

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

 

throw expr;

 

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

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

 

char *Buffer = new char [1000];

if (Buffer == 0)

throw "out of memory"; // брак пам'яті

 

Якщо виключення згенероване, але в програмі не передбачена його обробка, механізм виключень викликає з динамічної бібліотеки функцію завершення terminate(), яка видає повідомлення "Abnormal program termination" і припиняє виконання програми.

4.1.2 Блоки try і catch

Для обробки виникаючих виключень в програмі необхідно передбачити інструкції try і catch в наступній послідовності:

 

try

{

// послідовність інструкцій, при виконанні

// яких контролюється виникнення виключень

}

catch (тип1 arg)

{

// послідовність інструкцій,

// що обробляють виключення типу 1

}

catch (тип2 arg)

{

// послідовність інструкцій,

// що обробляють виключення типу 2

}

catch (типN arg)

{

// послідовність інструкцій,

// що обробляють виключення типу N

}

 

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

Якщо за try слідує відповідний блок catch, то управління переходить до нього.

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

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

Після виконання коду в блоці catch управління передається першій інструкції, наступній за всіма блоками catch, і програма відновлює роботу в нормальному режимі, якщо блок catch не містить оператора return або виклику функції exit().

Таким чином, інструкції try і catch запобігають завершенню програми стандартним обробником виключень.

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

4.1.3 Установка опції для включення механізму обробки виключень в програмах на Visual C++

При роботі в системі програмування Visual C++ для включення механізму обробки виключень в програмі, що розробляється, в конфігурації проекту має бути встановлена в Yes опція "Enable C++ Exceptions". При створенні нового проекту цей режим для обох конфігурацій (тестування і виконання) задається за умовчанням.

Щоб змінити установку, в меню Project потрібно вибрати підменю “ім'я_проекту Properties...”. У лівому вікні діалогу, що з'явився, потрібно відкрити папку Configuration Properties, якщо вона не відкрита, потім папку C/C++, після чого вибрати опцію Code Generation. Потім в таблиці, що з'явилася справа, змінити на потрібну установку рядка з написом Enable C/C++ Exceptions.

4.1.4 Приклад використання всіх інструкцій обробки виключень

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

 

try

{

// інструкції

char *Buffer = new char[1000];

if (Buffer == 0)

throw "out of memory"; // генерація виключення

// інструкції

}

catch (char *ErrorMsg)

{ // обробка виключення

cout << ErrorMsg << 'n';

// обробка помилки й відновлення виконання програми або

// виклик функції exit() для завершення програми

}

// тут виконання програми продовжується

 

В приведеному прикладі інструкція throw в якості виразу містить рядок "out of memory". Оскільки тип аргументу в інструкції catch (char * ErrorMsg) такий же, як і в інструкції throw, то даний блок catch одержує управління при генерації цього виключення.

В цьому прикладі інструкція catch оголошує аргумент типу char * з ім'ям ErrorMsg, що дозволяє всередині блоку catch отримати доступ до значення, заданого в інструкції throw.

Даний механізм дуже схожий на механізм передачі аргументів функції. Щоб упевнитися в цьому, потрібно представити інструкцію throw як виклик функції, в якому значення "Out of memory" передається як аргумент:

 

throw ("out of memory");

 

Блок catch в такому уявленні грає роль функції, що викликається, а оголошення char *ErrorMsg як оголошення її формальних аргументів. Як і аргументи функції, змінна ErrorMsg доступна тільки усередині блоку catch.

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

 

catch(char *)

{

// значення, задане в інструкції

// throw, використовуватися не може

}

 

Блок catch в цьому випадку, як і раніше, одержить управління по оператору throw "Out of memory", але без можливості доступу до переданого значення типу char*.

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

 

catch (int ErrorCode)

{

// управління до цього блоку не перейде при генерації

// виключення інструкцією throw "out of memory";

}

4.1.5 Управління декількома різнотипними виключеннями

Якщо за блоком try помістити декілька блоків catch, то можна управляти різними типами виключень. Наприклад, в наступному фрагменті програми обробляються виключення з аргументами типу char * або int:

 

try

{

throw "string"; // це виключення обробляється

// першим блоком catch

throw 5; // це виключення обробляється

// другим блоком catch

}

catch(char *ErrorMsg)

{

// обробка будь-якого виключення типу char *

}

catch (int ErrorCode)

{

// обробка будь-якого виключення типу int

}

 

Якщо оголошення блоку catch містить як аргумент три крапки, то такий блок одержує управління у відповідь на виключення будь-якого типу, які сгенеровані попереднім блоком try:

 

catch (...)

{

// одержує управління у відповідь

// на виключення будь-якого типу

}

 

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

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

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

Виключення Win32

Апаратні помилки, що генерують виключення Win32, це такі помилки, як ділення на нуль, некоректне звернення до пам'яті та інші. У відповідь на певні… Наприклад, якщо функції ::НеарА11ос() передається прапор… Для кожної функції Win32 API, яка може генерувати виключення, в документації MSDN описані всі випадки, в яких…

Обробка виключень Win32

  try {

МОДУЛЬНЕ ПРОГРАМУВАННЯ

При модульному програмуванні спочатку визначається, які модулі потрібні для вирішення завдання, а потім програма розбивається на декілька модулів… При розв'язанні задач, для яких не потрібне розділення даних на декілька… Оскільки оброблювані дані – тільки частина того, що хочеться приховати усередині модуля, принцип приховування даних…

Директиви препроцесора

  #include #define

Макроси

  #define ідентифікатор макроозначення #define ідентифікатор (список параметрів) макроозначення

Оператор defined

Оператор препроцесора defined може використовуватися в спеціальних константних виразах. Синтаксис таких виразів один з двох:

 

defined(ідентифікатор)

defined ідентифікатор

 

Значення такого виразу дорівнює true (не нуль), якщо вказаний ідентифікатор оголошений в програмі за допомогою директиви #define, та false (0) – інакше. Ідентифікатор, якому приписаний порожній рядок (тобто оголошений без літерала або макроозначення), вважається також оголошеним.

Оператор defined може використовуватися тільки в директивах умовної компіляції #if та #elif.

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

 

#if defined(CREDIT)

credit();

#elif defined(DEBIT)

debit();

#else

printerror();

#endif

6.4.2 Використання директив умовної компіляції

Директива препроцесора умовної компіляції #if багато в чому схожа на інструкцію if. Розглянемонаступний фрагмент програми:

 

#if !defined(NULL)

#define NULL 0

#endif

 

Перша з цих трьох директив визначає, чи не був оголошений раніше іменований літерал NULL. Вираз defined(NULL) дає значення 1, якщо літерал NULL оголошений, і 0 - інакше. Якщо результат рівний 0, то вираз !defined(NULL)дає значення 1 і в наступному рядку проводиться оголошення іменованого літерала NULL.Інакше директива #define пропускається. Кожен блок тексту з директивою #if має закінчуватися своєю директивою #endif.

Директиви:

 

#ifdef ідентифікатор

#ifndef ідентифікатор

 

є скороченим записом директив:

 

#if defined(ідентифікатор)

#if !defined(ідентифікатор)

 

і тому не потребують додаткових коментарів.

Можна використовувати складні конструкції умовних директив препроцесора, включаючи в ці конструкції директиви #else (еквівалент else в конструкції звичайної інструкції if) та #elif (еквівалент else if в конструкції звичайної інструкції if).

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

 

#if 0

Фрагмент коду, який не потрібно компілювати

#endif

 

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

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

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

Наприклад, в наступному фрагменті:

 

#ifdef DEBUG

cerr << "Змінна х = " << х << endl;

#endif

 

інструкція виводу в потік cerr компілюватиметься тільки у випадку, якщо ідентифікатор DEBUG був оголошений (директивою #define DEBUG)до директиви #ifdef DEBUG.Після завершення процесу налагодження директива #define DEBUG може бути просто видалена з початкового файлу, і інструкції виводу, потрібні тільки для цілей налагодження, ігноруватимуться під час компіляції. У великих програмах можливо потрібно буде оголошувати декілька різних ідентифікаторів, які можуть керувати умовною компіляцією різних частин початкового файлу.

6.5 Директива #error

Директива #error забезпечує видачу користувачу повідомлень під час компіляції, точніше, під час роботи препроцесора. Вона має наступний синтаксис:

 

#error текст_повідомлення

 

6.6 Директива #import

Директива #import використовується в системі програмування Visual C++ для включення в проект інформації з бібліотеки типів. При цьому вміст бібліотеки конвертується до класів C++, що представляють інтерфейси COM. Синтаксис використання директиви один з наступних:

 

#import “ім'я_файлу” атрибути

#import <ім'я_файлу> атрибути

 

6.7 Директива #line

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

 

#line номер “ім'я_файлу”

 

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

 

#line 100

 

задає початкове значення номера рядка, рівне 100, і все подальші рядки початкового тексту програми нумеруватимуться, починаючи з цього номера.

6.8 Директива #pragma

За допомогою директиви #pragma компілятор одержує вказівки (прагми|) по його роботі, пов'язані, можливо, з особливостями реалізації мови даним компілятором і використовуваною операційною системою. Синтаксис директиви наступний:

 

#pragma прагма|

 

Компілятор C++, що працює у складі Visual Studio .NET, підтримує наступні прагми| – вказівки по його роботі:

 

alloc_text

auto_inline

bss_seg

check_stack

code_seg

comment

component

conform

const_seg

data_seg

deprecated

function

hdrstop

include_alias

init_seg

inline_depth

in1ine_recursion

intrinsic

managed

message

once

optimize

pack

pointer_to_members

pop_macro

push_macro

runtime_checks

section

setlocale

unmanaged

vtordisp

warning

 

6.9 Директива #using

Ця директива імпортує в програму метадані, необхідні для роботи керованих розширень мови C++. Формат використання директиви:

 

#using <файл>

 

Файл, метадані якого імпортуються, має бути типу .dll, .exe, .netmodule або .obj.

Наприклад:

 

#using <mscorlib.dll>

6.10 Програмування на Асемблері

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

Для компіляції асемблерних інструкцій в системі програмування Visual C++ немає необхідності використовувати окрему програму – асемблер, таку як MASM (Microsoft Macro Assembler). Натомість потрібно скористатися асемблером, вбудованим у компілятор Visual C++. При цьому виключаються додаткові етапи асемблювання і компоновки роздільно компільованих програмних одиниць.

Компілятор Visual C++ забезпечує трансляцію всіх асемблерних інструкцій, зокрема тих, що містять всі коди операцій процесорів Pentium 4 і AMD Athlon. Слід при цьому відмітити, що асемблерні програми не завжди правильно працюватимуть на комп'ютерах з іншою архітектурою.

6.10.1 Інструкція __asm

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

За ключовим словом __asm повинна слідувати інструкція асемблера або список інструкцій асемблера, взятих у фігурні дужки:

 

__asm інструкція_асемблера ;

__asm

{

інструкція_асемблера1

інструкція_асемблера2

інструкція_асемблераN

};

 

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

Замість ключового слова __asm з двома символами підкреслення може використовуватися його варіант з одним символом підкреслення - _asm. Ключове слово asm без символів підкреслення, вживане для цих же цілей відповідно до стандарту на мову C++, може використовуватися в програмах на Visual C++, але воно не призводить до трансляції асемблерних інструкцій.

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

 

__asm

{

mov al, 2

mov dx, 0xD007

out dx, al

}

 

Цей блок може бути замінений послідовністю інструкцій __asm :

 

__asm mov al, 2

__asm mov dx, 0xD007

__asm out dx, al

 

Оскільки ключове слово __asm є роздільником інструкцій мови C++, то останній список інструкцій можна помістити в один рядок:

 

__asm mov al, 2 __asm mov dx, 0xD007 __asm out dx, al

 

Всі три варіанти інструкцій генерують один і той же двійковий код в програмі.

7 ОБ'ЄКТНО-ОРІЄНТОВАНЕ ПРОГРАМУВАННЯ

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

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

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

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

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

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

7.1 Класи. Інкапсуляція

Як було відзначено вище, класи в мові C++ введені як природне продовження і розвиток стандартних структур struct мови C, які є типами користувача.

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

Клас оголошується за допомогою ключового слова class. Синтаксис оголошення класу наступний:

 

class class_name

{

// закриті_члени_класу

public:

// відкриті_члени_класу

};

 

Клас мови C++ дуже схожий на стандартну структуру, хоча засоби, що надаються класами C++, значно перевершують можливості структур.

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

 

struct Frame

{

int Left;

int Top;

int Right;

int Bottom;

};

 

Далі можна визначити функцію малювання прямокутника:

 

void DrawFrame(Frame *Frm)

{

Line (Frm->Left, Frm->Top, Frm->Right, Frm->Top);

Line (Frm->Right, Frm->Top, Frm->Right, Frm->Bottom);

Line (Frm->Right, Frm->Bottom, Frm->Left, Frm->Bottom);

Line (Frm->Left, Frm->Bottom, Frm->Left, Frm->Top);

}

 

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

Нарешті, щоб намалювати прямокутник в заданому місці екрану, потрібно оголосити та ініціалізувати змінну типу Frame, а потім передати її у функцію малювання прямокутника DrawFrame:

 

Frame Frm = {25, 25, 100, 100};

DrawFrame (&Frm);

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

 

class CFrame

{

int Left;

int Top;

int Right;

int Bottom;

void Draw ()

{

Line (Left, Top, Right, Top);

Line (Right, Top, Right, Bottom);

Line (Right, Bottom, Left, Bottom) ;

Line (Left, Bottom, Left, Top);

}

};

 

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

В приведеному прикладі змінними – членами класу є цілі змінні Left, Top, Right і Bottom, а функцією – членом класу є функціяDraw().

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

7.1.1 Створення об'єктів класу

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

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

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

 

struct Frame

{

int Left;

int Top;

int Right;

int Bottom;

};

 

Щоб зарезервувати пам'ять і створити структурну змінну, потрібно зробити оголошення:

 

Frame Frm;

 

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

 

CFrame Frm;

 

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

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

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

Об'єкт класу можна створити, використовуючи наявний в мові C++ оператор new, наприклад:

 

CFrame *PFrm = new CFrame;

 

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

 

delete Frame;

 

Можна створити довільне число об'єктів кожного класу.

7.1.2 Доступ до членів класу

Доступ до членів класу організується операторами "." або "->", подібно доступу до елементів структури мови С. Доступом до членів класу можна керувати, використовуючи специфікатори доступу public (відкриті) або private (закриті). При цьому треба пам'ятати, що на відміну від структур змінні класу закриті за умовчанням.

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

Перш ніж йти далі, слід зазначити, що за наявності раніше приведеного оголошення класу Frame програма не зможе звернутися ні до одного з його членів, оскільки за умовчанням всі змінні та функції, що належать класу, є закритими (private). Це означає, що вони можуть використовуватися тільки всередині функцій-членів самого класу. Так, для функції Draw() дозволений доступ до змінних-членів Top, Left, Right і Bottom, тому що Draw() — функція-член класу. Для інших частин програми, таких як функція main(), доступ до змінних-членів або виклик функції-члена Draw() заборонений.

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

 

class CFrame

(

public:

int Left;

int Top;

int Right;

int Bottom;

void Draw ()

{

Line (Left, Top, Right, Top);

Line (Right, Top, Right, Bottom);

Line (Right, Bottom, Left, Bottom);

Line (Left, Bottom, Left, Top);

}

};

 

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

Тепер, коли всі члени класу CFrame відкриті, доступ до них, як і доступ до полів структури в мові C, можливий з використанням оператора "."

Наприклад:

 

CFrame Frm; // оголошення об'єкту класу CFrame

Frm.Left = 5; // присвоювання значень змінним-членам

Frm.Top =10; // для визначення координат прямокутника

Frm.Right = 100;

Frm.Bottom = 150;

Frm.Draw(); // малювання прямокутника

З іншого боку, можна створити об'єкт класу оператором new, а потім використати вказівник на об'єкт для доступу до змінних-членів класу. Для доступу до членів класу через вказівник, так само як і для структур, використовується оператор "->":

 

CFrame *PFrm = new CFrame;

PFrm->Left = 5;

PFrm->Top = 10;

PFrm->Right = 100;

PFrm->Bottom = 150;

PFrm->Draw();

7.1.3 Інкапсуляція

Згідно принципу інкапсуляції внутрішнє представлення даних, використане в реалізації класу, не повинно бути доступне користувачу класу безпосередньо.

Відповідно до цього принципу специфікатори доступу в мові C++ необхідно використовувати для запобігання безпосередньому доступу користувача до змінних-членів усередині класу. Можна створити функцію-член |, що змінює змінні-члени, але тільки після перевірки правильності значення, заданого користувачем.

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

З метою дотримання принципу інкапсуляції клас CFrame повинен бути оголошений так, щоб мати доступ до функцій-членів (у нашому випадку до Draw()), але не мати доступу до внутрішніх змінних-членів, використовуваних цими функціями (Left, Top, Right і Bottom):

 

class CFrame

(

private:

int Left;

int Top;

int Right;

int Bottom;

public:

void Draw ()

{

Line (Left, Top, Right, Top);

Line (Right, Top, Right, Bottom);

Line (Right, Bottom, Left, Bottom);

Line (Left, Bottom, Left, Top);

}

};

 

Специфікатор доступу private робить змінні, оголошені пізніше, закритими. Таким чином, вони доступні тільки функціям-членам класу. Подібно специфікатору доступу public, розглянутому раніше, специфікатор private впливає на всі оголошення, що стоять після нього, поки не зустрінеться інший специфікатор або кінець оголошення.

Отже, нове оголошення робить змінні Left, Top, Right і Bottom закритими, а функцію Draw() - відкритою.

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

Тут слід відмітити, що мова C++ надає ще й третій специфікатор доступу - protected. Цей специфікатор буде розглянутий пізніше, оскільки вимагає розуміння спадкування класів.

Наступний фрагмент програми ілюструє як коректне, так і некоректне звернення до членів чергового варіанту класу CFrame:

 

void main()

{

CFrame Frm; // оголошення об'єкту CFrame

Frm.Left = 5; // помилка: немає доступу до закритого члена

Frm.Top = 10; // помилка: немає доступу до закритого члена

Frm.Right = 100; // помилка: немає доступу до закритого члена

Frm.Bottom = 150; // помилка: немає доступу до закритого члена

Frm.Draw (); // допускається, але координати прямокутника

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

}

 

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

 

void SetCoord (int L, int T, int R, int B)

{

L = __min (__max (0, L), 80);

T = __min (__max (0, T), 25);

R = __min (__max (0, R), 80);

B = __min (__max (0, B), 25);

R = __max (R, L) ;

B = __max (B,T);

Left = L; Top = T; Right = R; Bottom = B;

}

 

Ця функціядодається в розділ public оголошення класу СFrame. Тому її можна викликати з будь-якої функції програми.

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

Макроси __max та __min надаються стандартною бібліотекою мови C++. Для їх використання в програму потрібно включити файл заголовків stdlib.h.

Тепер клас CFrame можна використовувати для створення прямокутників:

 

void main ()

{

CFrame Frm;

Frm.SetCoord (25, 25, 100, 100); // установка координат

// прямокутника

Frm.Draw (); // малювання прямокутника

}

 

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

 

void GetCoord (int *L, int *T, int *R, int *B)

{

*L = Left;

*T = Top;

*R = Right;

*B = Bottom;

}

 

Ця функція, так само як і функція SetCoord(), повинна бути додана в розділ public оголошення класу.

Нижче приведено остаточне оголошення класу СFrame, що містить нові функції-члени SetCoord() і GetCoord():

 

#include <сstdlib>

class СFrame

{

private:

int Left;

int Top;

int Right;

int Bottom;

public:

void Draw ()

{

Line (Left, Top, Right, Top);

Line (Right, Top, Right, Bottom);

Line (Right, Bottom, Left, Bottom) ;

Line (Left, Bottom, Left, Top);

}

void GetCoord (int *L, int *T, int *R, int *B)

{

*L = Left;

*T = Top;

*R = Right;

*B = Bottom;

}

void SetCoord (int L, int T, int R, int B)

{

L = __min (__max (0, L), 80);

T = __min (__max (0, T), 25);

R = __min (__max (0, R), 80);

В = __min (__max (0, B), 25);

R = __max (R, L) ;

В = __max (B,T);

Left = L; Top = T; Right = R; Bottom = B;

}

};

 

Тепер за допомогою функцій-членів SetCoord() та GetCoord() клас CFrame згідно принципу інкапсуляції надає доступ до закритих змінних тільки за допомогою чітко визначеного інтерфейсу, контролюючого коректність нових значень, що присвоюються, і, при необхідності, коректуючого ці значення.

7.1.3.1 Переваги інкапсуляції

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

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

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

 

private:

int Left;

int Top;

int Width;

int Height;

 

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

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

Конструктори

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

Конструктори за умовчанням

  class CFrame {

Деструктори

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

Правила спадкування

Похідний клас успадковує всі члени базового класу. У похідний клас можна додати нові члени для його настройки відповідно до вимог завдання. Варіанти спадкування в мові C++ і результуючий доступ у похідних класах до…  

Переваги спадкування

Перевага спадкування також полягає в спрощенні налагодження і супроводу програм, оскільки код і дані, що управляють виконанням завдання, не… У мові C++ за допомогою ієрархії класів можна відтворити модель взаємин… 7.2.8 Приклади бібліотек класів і множинне спадкування

Приклад перевантаження оператора

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

 

class CMoney

{

private:

long Grivnas;

int Copecks;

public:

CMoney ()

{

Grivnas = Copecks = 0;

}

CMoney (long Grn, int Cop)

{

SetAmount (Grn, Cop);

}

void GetAmount (long *PGrn, int *PCop)

{

*PGrn = Grivnas;

*PCop = Copecks;

}

void PrintAmount ()

{

cout.fill ('0') ;

cout.width (1);

cout << 'Г' << Grivnas << '.';

cout.width (2);

cout << Copecks << 'n';

}

void SetAmount (long Grn, int Cop)

{

// перевірка суми копійок, яка перевищує 100

Grivnas = Grn + Cop / 100;

Copecks = Cop % 100;

}

};

 

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

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

Передбачені окрема функція-член SetAmount() для завдання величини грошової суми, а також функції-члени GetAmount() і PrintAmount() для підрахунку суми та її друку, відповідно.

Можна було б зберігати значення суми в одній змінній типу long у вигляді загальної суми в копійках, що потребувало би обробки великих значень у копійках. Проте краще для зберігання величини, що позначає кількість гривень, використовувати змінну long, а для копійок — змінну типу int. В результаті можна буде оперувати кількістю гривень, що досягає максимальної величини типу long, що в системі програмування Visual C++ складає 2147483647.

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

Для перевантаження оператора визначається функціяз ім'ям operator, за яким як суфікс слідує символ оператора.

Наприклад, можна перевантажити оператор "+", додавши до оголошення класу CMoney наступну функцію-член|:

 

class CMoney

{

// інші оголошення

public:

CMoney operator+(CMoney Mon)

{

CMoney Temp(Grivnas + Mon.Grivnas, Copecks + Mon.Copecks);

return Temp;

}

// інші оголошення

};

 

Функція-оператор "+" визначена як public, щоб її можна було використовувати в інших частинах програми.

Якщо функція-оператор "+" визначена для класу, операцію складання грошових значень можна реалізувати таким чином:

 

CMoney Amountl (12, 95);

CMoney Amount2 (4, 38);

CMoney Total;

Total = Amountl + Amount2;

 

Компілятор мови C++ інтерпретує вираз:

 

Amountl + Amount2

 

як:

 

Amountl.operator+(Amount2)

 

Функція operator+() створює тимчасовий об'єкт Temp класу CMoney, що містить розмір грошової суми, одержаної в результаті складання двох об'єктів. Потім вона повертає тимчасовий об'єкт. Наведений вище фрагмент програми присвоює об'єкт, що повертається функцією operator+(), об'єкту Total класу CMoney. Такий спосіб присвоювання робиться можливим завдяки поелементному копіюванню компілятором змінних-членів об'єкту одного класу в іншій об’єкт цього ж класу.

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

 

void main ()

{

CMoney Advertising (235, 42);

CMoney Rent (823, 68);

CMoney Entertainment (1024, 32);

CMoney Overhead;

Overhead = Advertising + Rent + Entertainment;

Overhead.PrintAmount();

}

 

Реалізацію функції operator+() можна спростити, замінивши локальний тимчасовий об'єкт класу CMoney неявним тимчасовим об'єктом:

 

CMoney operator+(CMoney Mon)

{

return CMoney (Grivnas + Mon.Grivnas, Copecks + Mon.Copecks);

}

 

При виклику конструктора класу в інструкції return компілятор створює тимчасовий об'єкт класу, після чого функціяoperator+() безпосередньо повертає вміст цього тимчасового об'єкту.

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

Наступний фрагмент програми є остаточною версією визначення функції operator+():

 

CMoney operator+(const CMoney &Mon)

{

return CMoney(Grivnas + Mon.Grivnas, Copecks + Mon.Copecks);

}

 

Використання специфікатора const при оголошенні аргументу - гарантія того, що функціяне змінить значення цього аргументу.

7.4.2 Визначення перевантажених функцій-операторів

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

Наприклад, за допомогою перевантаженого оператора "+" можна складати об'єкт класу CMoney із числами типу int або long, що позначають ціле число гривень. Для цього в клас CMoney потрібно включити наступну функцію:

 

CMoney operator+(long Grn)

{

return CMoney (Grivnas + Grn, Copecks);

}

 

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

 

CMoney Advertising (235, 42);

// ...

Advertising = Advertising + 100;

 

Компілятор інтерпретуватиме вираз Advertising + 100 як Advertising.operator+(100) і, отже, викликатиме тільки що визначену версію функції operator+().

Відмітимо, що визначена першою функція-оператор "+" має на увазі, що обидва операнди функції є об'єктами класу CMoney.

7.4.3 Дружні функції і класи

У інструкції останнього прикладу:

 

Advertising = Advertising + 100;

 

цілочисельну константу не можна поставити першою, оскільки компілятор інтерпретуватиме вираз 100 + Advertising як:

 

100.operator+(Advertising),

 

що є безглуздим.

Щоб обійти це обмеження, можна написати функцію-оператор, перший аргумент якої має тип long і яка не є членом класу:

 

// визначається глобально

CMoney operator+ (long Grn, const CMoney &Mon)

{

return CMoney(Grn + Mon.Grivnas, Mon.Copecks);

}

 

Але при компіляції цієї функції виникнуть проблеми, оскільки вона не є функцією-членом класу CMoney і не має доступу до закритих змінних цього класу, а саме до Grivnas і Copecks.

Для отримання такого доступу функцію потрібно зробити дружньою класу CMoney, оголосивши її всередині оголошення CMoney з використанням специфікатора friend:

 

class CMoney

{

// інші оголошення

friend CMoney operator+ (long Grn, const CMoney &Mon);

// інші оголошення

};

 

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

Коли така дружня функція-оператор визначена, операцію складання можна застосувати таким чином:

 

CMoney Advertising (235, 42);

// ...

Advertising = 100 + Advertising;

 

Тепер компілятор проінтерпретує вираз:

 

100 + Advertising

 

як:

 

operator+(100, Advertising)

 

і, отже, викличе дружню версію функції-оператора.

Можна було б зробити дружніми класу і перші два варіанти функції operator+(), не визначаючи їх як функції-члени класу, хоча особливої переваги це не дає:

 

friend CMoney operator + (const CMoney &Monl,

const CMoney &Mon2);

friend CMoney operator + (const CMoney SMon, long Grn);

 

В оголошенні класу також можна оголосити дружнім інший клас, наприклад:

 

class A

{

// …

friend class FriendOfA;

// …

};

 

Завдяки такому оголошенню будь-яка функція-член класу FriendOfA має доступ до закритих і захищених членів класу А.

7.4.4 Загальні принципи перевантаження операторів

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

Можна перевантажити ледь не кожний з існуючих бінарних і унарних операторів мови C++.

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

У табл. 7.2 перелічені оператори мови C++, що допускають перевантаження.

 

Таблиця 7.2

Перелік операторів мови C++, які можна перевантажувати

+ - * / % ^ & |
~ ! = < > += -= *=
/= %= ^= &= |= << >> <<=
>>= == != <= >= && || ++
-- ->* , -> [] () new delete
new[] delete[]            

 

У таблиці 7.3 перелічені оператори мови C++, що не допускають перевантаження:

 

Таблиця 7.3

Перелік операторів мови C++, що не допускають перевантаження

. .* :: ?: sizeof

 

Приклади перевантаження бінарних операторів детально були розглянуті раніше.

Використовуючи функцію-член| класу без аргументів, можна перевантажити й унарний оператор. Для класу CMoney це виглядатиме наступним чином:

 

// задається в оголошенні класу CMoney

CMoney operator-() // унарний оператор “-” (негативність)

{

return CMoney(-Grivnas, -Copecks);

}

 

Замість функції-члена унарний оператор можна перевантажити, використовуючи дружню функцію, яка не належить класу і має єдиний аргумент:

 

// визначається глобально

CMoney operator- (CMoney &Mon)

{

return CMoney (-Mon.Grivnas, -Mon.Copecks);

}

 

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

 

friend CMoney operator-(CMoney &Mon);

 

Наведені приклади ілюструють загальні принципи перевантаження операторів.

Проте, при перевантаженні деяких операторів (наприклад, "++") потрібно дотримуватись спеціальних правил.

7.4.5 Конструктори копіювання і приведення

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

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

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

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

Наприклад, якщо клас CTest має конструктор:

 

CTest(int Parm)

{

// код конструктора

}

 

то можна створити об'єкт, використовуючи інструкцію:

 

CTest Test(5);

 

або еквівалентну по дії інструкцію:

 

CTest Test = 5;

 

Використання знаку рівності — альтернативний спосіб передачі єдиного значення конструктору. Оскільки вона реалізується конструктором, то ця операція є операцією ініціалізації, а не присвоювання, тому перевантаження оператора "=" не зачіпає виконання даної операції.

7.4.6 Конструктори копіювання

Конструктор копіювання класу — це конструктор з єдиним аргументом, тип якого оголошений як посилання на цей же клас.

Наприклад:

 

class CTest

{

// …

public:

CTest(const CTest &Test)

{

// інструкції використовують члени вже наявного об'єкту Test

// класу CTest для ініціалізації нового об'єкту класу CTest

}

// …

};

 

Якщо конструктор копіювання класу не оголошений, то компілятор генерує його неявно.

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

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

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

 

CMoney Monl (95, 34);

CMoney Mon2 (Monl);

CMoney Mon3 = Monl;

 

Ініціалізація об'єкту Mon2, як і Mon3, викликає конструктор копіювання, що згенерований компілятором. В результаті даної ініціалізації обидва об'єкти Mon2 і Mon3 міститимуть ті ж значення, що й Monl (тобто змінна Grivnas буде рівна 95, а Copecks — 34).

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

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

 

class CMessage

{

// …

public:

CMessage (const CMessage &Message)

{

Buffer = new char [strlen (Message.Buffer) + 1];

strcpy (Buffer, Message.Buffer);

}

// …

};

 

Такий конструктор копіювання дозволяє ініціалізувати об'єкти, як це показано в наступному фрагменті програми:

 

CMessage Messagel;

Messagel.Set ("hello");

CMessage Message2(Messagel); // використовується конструктор

// копіювання

CMessage Message3 = Messagel;// використовується той же конструктор

// копіювання

 

Крім операцій копіювання компілятор автоматично викликає конструктор копіювання класу в двох інших випадках:

- при передачі об'єкту класу як аргументу функції;

- при поверненні функцією об'єкту класу.

Як приклад розглянемо раніше визначену функцію-оператор:

 

CMoney operator+(CMoney Mon)

{

return CMoney(Grivnas + Mon.Grivnas, Copecks + Mon.Copecks);

}

 

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

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

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

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

Конструктори приведення

Наприклад, в клас CMoney можна додати конструктор приведення, який дасть можливість ініціалізувати об'єкт, використовуючи суму гривень і копійок,…   class CMoney

УЗАГАЛЬНЕНЕ ПРОГРАМУВАННЯ

Шаблони - це спосіб написання єдиного узагальненого визначення функції або класу, яке компілятор автоматично транслює в спеціальну версію функції… При програмуванні| задач з використанням шаблонів спочатку потрібно вирішити,… Слід зазначити, що засоби узагальненого програмування до теперішнього часу (2004 рік) відсутні у всіх інших мовах…

Клас string

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

Контейнери

Ми можемо узагальнити стек символів до стека елементів будь-якого типу, оголосивши його як шаблон і замінивши конкретний тип char на параметр…   template<class T> class stack

Потоки вводу - виводу

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

Класи потокового вводу - виводу

Система вводу – виводу мови C++ будується на два зв'язаних, але різних ієрархіях класів-шаблонів. Перша ієрархія класів є похідною від класу нижнього рівня basic_streambuf.Цей… Ієрархія класів, з якою програми частіше за все мають справу, є похідною від класу basic_ios.Це клас вводу - виводу…

Прапори формату

  adjustfield floatfield right skipws basefield hex scientific unitbuf …   Всі ці значення оголошені в класі ios і необхідні для установки або очистки прапорів формату.

Закриття файлу

  mystream.close ();  

Контроль стану вводу - виводу

goodbit – помилок немає; eofbit – досягнутий кінець файлу; failbit – не фатальна помилка;

Атрибути

Атрибути беруться в квадратні дужки і можуть встановлюватися для класів, членів класів, аргументів та інших елементів програми. Приклад використання…   [CodeReview("l/l/199", Comment="Rockin")]

Мова програмування Visual Basic .NET

10.3.1 Інструкції мови VB Блоки інструкцій мови VB завершуються інструкцією END, і декілька інструкцій…  

Оператори та вирази

Оператори С++ дещо відрізняються від операторів VB, тому деякі вирази можуть показатися незнайомими. В табл. 10.2 приведені основні| оператори VB, що відрізняються за позначенням або за сенсом від відповідних операторів мови C++.

Таблиця 10.2

Оператори мови VB

Оператор VB Еквівалент С++
^ (зведення в ступінь) Немає
Mod (залишок від ділення) %
& (конкатенація рядків) +
= (рівно) ==
<> (не рівно) !=
Like (подібно) Немає
Is (є) Немає
And &&
Or ||
Xor ^
Imp Немає

10.3.4 Управління послідовністю виконання інструкцій програми

В мовах С++ та VB використовуються схожі управляючі структури, але їх синтаксис дещо відрізняється.

10.3.4.1 Інструкція If Then

В мові С++ не існує інструкції Then. Після умови відразу ж іде інструкція або блок інструкцій, що виконуються в разі істинності умови, а потім йде необов'язкова інструкція else.

Розглянемо наступний фрагмент програми на VB:

 

If size < 60 Then

value = 50

Else

value = 55

order = 12

End If

 

На мові С++ цей фрагмент програмується так:

 

if (size < 60)

value = 50;

else

{

value = 55;

order = 15;

}

 

В мові С++ не існує інструкції ElseIf, яка є в мові VB.

Цикл For

  For i = 1 to 100 ' Деякі дії

РЕКОМЕНДОВАНА ЛИТЕРАТУРА

1. Б. Страуструп. Язык программирования С++. Специальное издание. Пер. с англ. – М.: ООО «Бином-Пресс», 2004 г. 1104 с.

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

3. Шилдт, Герберт. Полный справочник по C++. 4-е издание.: Пер. с англ. – М.: Издательский дом «Вильямс», 2003. – 800 с.

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

5. К. Паппас, У. Мюррей. Эффективная работа: Visual C++ .NET. – СПб.: Питер, 2002. – 816 с.

6. Керниган Б., Ритчи Д., Фьюэр А. Язык программирования Си / Пер. с англ. Д. Б. Подшивалова и В. А. Иващенко. – М.: Финансы и статистика, 1985. – 279 с.

7. Г. Шилдт. Самоучитель C++ / Пер. с англ. 3-е изд. – СПб.:БХВ – Петербург, 2002. – 688 с.

8. Шефферд Джордж. Программирование на Microsoft Visual C++ .NET /Пер. с англ. – М.: Издательско-торговый дом «Русская редакция», 2003. – 928 с.

9. Черносвитов А. Visual C++ 7: учебный курс. – СПб.: Питер, 2001. – 528 с.

10. Плаугер П., Степанов А., Ли М., Массер Д. STL – стандартная библиотека шаблонов C++ / Пер. с англ.. – СПб.: БХВ-Петербург, 2004. – 656 с.

11. Троелсен Э. C# и платформа .NET. Библиотека программиста. – СПб.: Питер, 2002. – 800 с.

12. Корнелл Г., Моррисон Дж. Программирование на VB.NET: учебный курс. – СПб.: Питер, 2002. – 400 с.


 

ВСТУП.. 2

1 БАЗОВІ ЕЛЕМЕНТИ МОВИ СИСТЕМНОГО ПРОГРАМУВАННЯ 4

1.1 Функції 5

1.2 Коментарі 5

1.3 Змінні і типи даних.. 6

1.4 Вказівники і масиви.. 8

1.5 Літерали.. 9

1.5.1 Логічні літерали. 9

1.5.2 Символьні літерали. 9

1.5.3 Цілі літерали. 10

1.5.4 Літерали з плаваючою крапкою.. 10

1.5.5 Рядкові літерали. 11

1.6 Вирази.. 11

1.7 Оператори.. 12

1.7.1 Оператори вводу – виводу. 18

1.7.1.1 Оператор вводу. 18

1.7.1.2 Оператор виводу. 19

1.8 Інструкції 20

2 РОБОТА З ДАНИМИ.. 23

2.1 Оголошення. 23

2.1.1 Синтаксис оголошення. 24

2.1.2 Оголошення декількох імен. 25

2.1.3 Ідентифікатори. 25

2.1.4 Область видимості імен. 27

2.1.5 Час життя об'єктів. 28

2.1.6 Ініціалізація об'єктів. 29

2.1.7 Специфікатори сталості змінних. 30

2.1.7.1 Специфікатор const 30

2.1.7.2 Специфікатор volatile. 30

2.1.8 Розміщення іменованих об'єктів в пам'яті 31

2.1.8.1 Регістрова пам'ять. Специфікатор register 31

2.1.8.2 Локальна пам'ять. Специфікатор auto. 32

2.1.8.3 Статична пам'ять. Специфікатор static. 32

2.1.8.4 Специфікатор extern. 33

2.1.9 Динамічна пам'ять. 35

2.1.9.1 Оператори new і delete. 35

2.1.9.2 Оператори new[] і delete[] 36

2.1.9.3 Обробка помилок виділення пам'яті 37

2.1.9.4 Автоматичне збирання сміття. 38

2.1.9.5 Фрагментація пам'яті 39

2.2 Вказівники.. 40

2.2.1 Вказівники та константи. 42

2.2.2 Вказівник на void. 43

2.3 Посилання. 44

2.4 Масиви.. 45

2.4.1 Ініціалізація масивів. 47

2.4.2 Вказівники й масиви. 48

2.4.3 Доступ до елементів масиву. 49

2.5 Типи даних користувача.. 51

2.5.1 Переліки. 51

2.5.2 Структури мови C.. 53

2.5.2.1 Вибір члена структури по імені об'єкту. 56

2.5.2.2 Вибір члена структури через вказівник на структурний об'єкт 57

2.5.2.3 Еквівалентність структурних типів. 57

2.5.2.4 Поля. 57

2.5.3 Об'єднання. 59

2.5.4 Оголошення typedef 61

2.6 Неявні приведення типів. 62

2.7 Явні приведення типів. 63

2.7.1 Традиційний стиль явного приведення типів. 63

2.7.2 Приведення типів в стилі мови C++.. 64

2.7.2.1 Оператор dynamic_cast 64

2.7.2.2 Оператор static_cast 65

2.7.2.3 Оператор reinterpret_cast 66

2.7.2.4 Оператор const_cast 66

3 ФУНКЦІЇ. ПРОЦЕДУРНЕ ПРОГРАМУВАННЯ.. 68

3.1 Оголошення функцій.. 68

3.2 Визначення функцій.. 69

3.2.1 Вмонтовані (inline) функції 70

3.3 Передача аргументів при виклику функції 71

3.3.1 Використання посилань як аргументів. 72

3.3.2 Масиви як аргументи. 73

3.3.3 Аргументи за умовчанням.. 74

3.3.4 Неоголошені аргументи. 75

3.4 Значення, що повертається функцією... 76

3.5 Рекурсивні функції 77

3.6 Перевантаження функцій.. 78

3.7 Вказівники на функцію... 81

3.8 Функція main() 83

3.8.1 Перехід від main() до _tmain() 84

3.9 Внутрішня реалізація викликів і повернень з функцій.. 84

3.9.1 Ключові слова __cdecl, __stdcall та __fastcall 85

3.9.2 Макрос WINAPI 86

3.10 Функції вводу – виводу мови C.. 86

3.10.1 Форматний ввід - вивід для стандартних потоків вводу і виводу 86

3.10.1.1 Функція printf() 87

3.10.1.2 Функція scanf() 91

3.10.2 Форматні перетворення в пам'яті. 93

3.10.3 Функції роботи з файлами. 93

3.10.3.1 Форматний ввід - вивід для файлів. 94

3.10.4 Функції посимвольного вводу – виводу. 94

4 ПРОГРАМУВАННЯ ВИКЛЮЧЕНЬ.. 96

4.1 Загальна схема обробки виключень. 96

4.1.1 Інструкція throw.. 96

4.1.2 Блоки try і catch. 97

4.1.3 Установка опції для включення механізму обробки виключень в програмах на Visual C++ 98

4.1.4 Приклад використання всіх інструкцій обробки виключень 98

4.1.5 Управління декількома різнотипними виключеннями. 100

4.2 Виключення Win32. 101

4.2.1 Обробка виключень Win32. 103

5 МОДУЛЬНЕ ПРОГРАМУВАННЯ.. 104

5.1 Простори імен.. 104

5.1.1 Інструкція namespace. Оператор розширення області видимості (::) 105

5.1.2 Інструкція using. 107

5.2 Роздільна компіляція. 108

5.2.1 Директива #include. 109

6 МАКРОЗАСОБИ. ПРОГРАМУВАННЯ НА АСЕМБЛЕРІ. 112

6.1 Директиви препроцесора.. 112

6.2 Директива #define. 113

6.2.1 Іменовані літерали. 113

6.2.2 Макроси. 114

6.3 Директива #undef. 116

6.4 Директиви умовної компіляції #if, #ifdef, #ifndef, #elif, #else і #endif 117

6.4.1 Оператор defined. 117

6.4.2 Використання директив умовної компіляції 118

6.5 Директива #error. 119

6.6 Директива #import. 120

6.7 Директива #line. 120

6.8 Директива #pragma.. 120

6.9 Директива #using.. 121

6.10 Програмування на Асемблері 122

6.10.1 Інструкція __asm.. 122

7 ОБ'ЄКТНО-ОРІЄНТОВАНЕ ПРОГРАМУВАННЯ.. 124

7.1 Класи. Інкапсуляція. 124

7.1.1 Створення об'єктів класу. 127

7.1.2 Доступ до членів класу. 128

7.1.3 Інкапсуляція. 130

7.1.3.1 Переваги інкапсуляції 133

7.1.4 Конструктори. 134

7.1.4.1 Конструктори за умовчанням.. 136

7.1.4.2 Перевантажені конструктори. 137

7.1.4.3 Списки ініціалізації 138

7.1.5 Деструктори. 139

7.1.6 Виклики конструкторів і деструкторів. 141

7.1.7 Константні об'єкти і функції-члени класу. 141

7.1.8 Статичні члени класу. 143

7.1.9 Вбудовані функції-члени класу. 146

7.1.10 Вказівник this. 148

7.1.11 Масиви об'єктів. 150

7.1.12 Ієрархії об'єктів. 151

7.1.13 Структури і об'єднання в мові C++.. 152

7.2 Похідні класи. Спадкування. 153

7.2.1 Похідні класи. 153

7.2.2 Конструктори похідних класів. 156

7.2.3 Порядок виклику конструкторів і деструкторів. 157

7.2.4 Успадковані змінні 158

7.2.5 Правила спадкування. 160

7.2.6 Ієрархії класів. 161

7.2.7 Переваги спадкування. 162

7.2.8 Приклади бібліотек класів і множинне спадкування. 163

7.3 Віртуальні функції. Поліморфізм.. 163

7.3.1 Вказівники на базові класи. 164

7.3.2 Віртуальні функції 164

7.3.3 Поліморфізм.. 168

7.3.4 Застосування віртуальних функцій для управління об'єктами різних класів 168

7.3.5 Застосування віртуальних функцій для модифікації базових класів 170

7.3.6 Механізм перевизначення базових класів. 172

7.3.7 Конкретні й абстрактні класи. 173

7.3.8 Внутрішня реалізація віртуальних функцій та поліморфізму 175

7.4 Перевантаження операторів. 177

7.4.1 Приклад перевантаження оператора. 177

7.4.2 Визначення перевантажених функцій-операторів. 180

7.4.3 Дружні функції і класи. 181

7.4.4 Загальні принципи перевантаження операторів. 183

7.4.5 Конструктори копіювання і приведення. 184

7.4.6 Конструктори копіювання. 185

7.4.7 Конструктори приведення. 188

7.5 Динамічна ідентифікація типу.. 191

8 УЗАГАЛЬНЕНЕ ПРОГРАМУВАННЯ.. 194

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

8.1.1 Створення екземпляра функції за шаблоном.. 196

8.1.2 Спеціалізація шаблонів функцій. 198

8.2 шаблони класів. 199

8.2.1 Роздільна реалізація функцій – членів шаблонів. 202

8.2.2 Створення об'єктів за шаблоном класів. 202

8.2.3 Конструктори та деструктори шаблонів класів. 204

8.2.4 Конкретизація і спеціалізація шаблонів класів. 207

8.3 Клас string.. 207

8.3.1 Основні конструктори. 210

8.3.2 Основні оператори. 210

8.4 STL - стандартна бібліотека шаблонів мови C++. 212

8.4.1 Контейнери. 212

8.4.2 Послідовності та ітератори. 214

8.4.3 Узагальнені алгоритми. 215

9 СИСТЕМА ВВОДУ-ВИВОДУ МОВИ C++. 218

9.1 Потоки вводу - виводу.. 218

9.2 Класи потокового вводу - виводу.. 219

9.3 Форматний ввід - вивід.. 220

9.3.1 Прапори формату. 220

9.3.2 Функції установки і очистки прапорів формату setf() та unsetf() 221

9.3.3 Функція читання і установки всіх прапорів формату flags() 223

9.3.4 Функції width(), precision() і fill() 224

9.3.5 Маніпулятори вводу - виводу. 226

9.4 Файловий ввід – вивід.. 229

9.4.1 Відкриття файлу. 230

9.4.2 Закриття файлу. 232

9.4.3 Функція eof() 233

9.4.4 Операції читання і запису для файлів. 233

9.5 Двійковий неформатний ввід - вивід.. 234

9.5.1 Основні функції двійкового вводу - виводу. 234

9.5.2 Додаткові функції двійкового вводу - виводу. 236

9.6 Довільний доступ.. 239

9.7 Контроль стану вводу - виводу.. 241

9.8 Системи вводу – виводу користувача.. 243

9.8.1 Функції виводу користувача. 243

9.8.2 Функції вводу користувача. 245

9.8.3 Маніпулятори користувача. 247

9.8.4 Ввід – вивід користувача і файли. 249

10 КЕРОВАНА МОВА C++ ТА ІНШІ МОВИ ПРОГРАМУВАННЯ ПЛАТФОРМИ MICROSOFT .NET 250

10.1 Керований C++. 250

10.2 Мова програмування C#. 252

10.2.1 Робота з об'єктами. 252

10.2.2 Інструкції 252

10.2.3 Атрибути. 253

10.2.4 Робота з версіями. 253

10.2.5 Організація програм.. 254

10.2.6 Відсутні в мові C# елементи. 254

10.3 Мова програмування Visual Basic .NET. 254

10.3.1 Інструкції мови VB.. 254

10.3.2 Типи даних та змінні 255

10.3.3 Оператори та вирази. 255

10.3.4 Управління послідовністю виконання інструкцій програми 256

10.3.4.1 Інструкція If Then. 256

10.3.4.2 Цикл For 257

10.3.4.3 Інструкція For Each. 257

10.3.4.4 Інструкція Do Loop. 257

10.3.4.5 Інструкція Select Case. 258

10.3.4.6 Інструкція On Error 259

РЕКОМЕНДОВАНА ЛИТЕРАТУРА.. 260

 

– Конец работы –

Используемые теги: Конспект, лекцій, СИСТЕМНЕ, програмування0.041

Если Вам нужно дополнительный материал на эту тему, или Вы не нашли то, что искали, рекомендуем воспользоваться поиском по нашей базе работ: Конспект лекцій СИСТЕМНЕ ПРОГРАМУВАННЯ

Что будем делать с полученным материалом:

Если этот материал оказался полезным для Вас, Вы можете сохранить его на свою страничку в социальных сетях:

Еще рефераты, курсовые, дипломные работы на эту тему:

КОНСПЕКТ ЛЕКЦІЙ з дисципліни Економічна і соціальна географія світу Конспект лекцій з дисципліни Економічна і соціальна географія світу розроблений викладачем 1 категорії Рибаченко І.М. Затверджений на засіданні циклової комісії загальноосвітніх дисциплін
МІНІСТЕРСТВО НАУКИ І ОСВІТИ УКРАЇНИ Верстатоінструментальний технікум... НАЦІОНАЛЬНОГО ТЕХНІЧНОГО УНІВЕРСИТЕТУ... ХПІ...

Конспект лекцій з курсу Управлінський облік Конспект лекцій дає змогу ознайомитися з основами сучасного обліку й навчитися їх практичному застосуванню
ХАРКІВСЬКА НАЦІОНАЛЬНА АКАДЕМІЯ... МІСЬКОГО ГОСПОДАРСТВА... Конспект лекцій з курсу Управлінський облік...

З курсу Моделювання та прогнозування як конспект лекцій з дисципліни Конспект лекцій
Сумський державний університет... Конспект лекцій з курсу Моделювання та прогнозування...

Лекція 1. Сенс визначення філософія Сходу у контексті протиставлення європейській історії філософії. Лекція 2. Витоки філософії Індії. Лекція 3. Санкх’я та йога Патанджалі. Лекція 4. Вайшешика і ньяя
Стародавність та Середньовіччя... ЗМІСТ... ВСТУП Лекція Сенс визначення філософія Сходу у контексті протиставлення європейській історії філософії...

Конспект лекцій як конспект лекцій з курсу Експлуатація та обслуговування машин
СУМСЬКИЙ ДЕРЖАВНИЙ УНІВЕРСИТЕТ... ЕКСПЛУАТАЦІЯ ТА ОБСЛУГОВУВАННЯ МАШИН...

Опорний конспект лекцій Опорний конспект лекцій Філософія
Дніпропетровський державний фінансово економічний інститут... Л М Табінська...

Конспект лекционного курса по дисциплине Философия Конспект лекцій
Українська інженерно педагогічна академія... ФІЛОСОФІЯ Конспект лекцій...

Конспект лекцій Змістовий модуль 1. Психологія і педагогіка як наука про людину, її світ і діяльність. Лекція 1 Тема І. Теоретичні основи психології та педагогіки
Змістовий модуль Психологія і педагогіка як наука про людину її світ і діяльність... Лекція...

Лекція №1. Моніторинг та розвиток виникнення надзвичайних ситуацій. Лекція №2. Характеристика вогнищ ураження та методи розрахунку зон ураження від техногенних вибухі. Лекція №3. Оцінка обстановки надзвичайної ситуації
Житомирський державний університет імені Івана Франка... Фiзико математичний факультет Кафедра охорони... ЗМІСТ Вступ Лекція Моніторинг та розвиток виникнення надзвичайних ситуацій...

ТЕМАТИКА КУРСУ ЛЕКЦІЙ ІСТОРІЯ УКРАЇНИ. Ввідна лекція. 1.Предмет і завдання курсу. Періодизація історії України
Ввідна лекція... План...

0.022
Хотите получать на электронную почту самые свежие новости?
Education Insider Sample
Подпишитесь на Нашу рассылку
Наша политика приватности обеспечивает 100% безопасность и анонимность Ваших E-Mail
Реклама
Соответствующий теме материал
  • Похожее
  • По категориям
  • По работам