C _Учебник_МОНУ
.pdfФункції |
299 |
Функції не можуть бути перевантажені, якщо опис їхніх параметрів відрізняється лише специфікатором const чи використанням посилання, наприклад int та const int чи int та int&.
Приклад 8.21 Написати дві функції для обчислення суми сімох елементів цілого й дійсного масивів відповідно. Використати ці функції для обчислення сум двох масивів.
Тексти функцій та їх викликів в основній пр ограмі: int adder(int iarray[7])
{int isum = 0;
for(int i=0; i<7; i++)
isum += iarray[i]; return(isum);
}
float adder (float farray[7])
{float fsum = 0;
for(int i= 0; i < 7; i++)
fsum += farray[i]; return (fsum) ;
}
void main() {
int iarray[7] = {5,1,6,20,15,0,12};
float farray[7] = {3.3,5.2,0.05,1.49,3.12345,31.0,2.007}; int isum; float fsum;
isum = adder (iarray); // Виклик для масиву цілих чисел fsum = adder (farray); // Виклик для масиву дійсних чисел
cout << "Сума масиву цілих чисел дорівнює " << isum << "\n"; cout << "Сума масиву дійсних чисел дорівнює "<< fsum << "\n"; getch ();
}
Результати виконання програми:
Сума масиву цілих чисел дорівнює 59 Сума масиву дійсних чисел дорівнює 46.1705
Приклад 8.22 Написати три функції для обчислення периметра трикутника, чотирикутника й п‟ятикутника відповідно.
Розв‟язок. Завдання відрізняється від попереднього тим, що тип параметрів є однаковий, а кількість параметрів – різна. При викликанні функції обиратиметься той варіант, для якого збігається кількість фактичних і формальних параметрів.
Тексти функції та її виклику в основній пр ограмі:
//Визначення всіх потрібних версій функції
//Версія функції для трикутника
int Perimetr(int Len1, int Len2, int Len3) { return Len1 + Len2 + Len3; }
Функції |
301 |
13)Що таке область видимості змінної?
14)Чи можна у С++ оголошувати вкладені функції?
15)Які є способи передавання параметрів до функції?
16)Що буде виведено на екран після виконання програми у завданні 12,
якщо замінити функцію swap() на таку: void swap(int &i, int &j)
{int temp = i; i = j; j = temp; }
17)Що таке перевантаження функцій?
18)Яка проблема виникне з такими перевантаженими функціями? void inc(int &i)
{i = i + 1; }
void inc(int &i, int diff = 1)
{i += diff; }
19)Знайдіть помилку у функції
double volume(double length, double width = 1, double height) { return length * width * height; }
20)Поясніть способ передавання параметрів до функції void inc (int &i, int diff = 1)
{i += diff; }
21)Яка помилку припущено у такій програмі? Як її виправити?
# include <iostream.h> int main()
{double x = 5.2;
cout << x << " ^ 2 = " << sqr(x); return 0;
}
double sqr( double х)
{return x * x; }
22)Що таке рекурсія?
23)Чи є наступна рекурсивна функція правильною? Поясніть. double F(double х)
{return F(x-1); }
24)Напишіть програму із застосуванням перевантаження функцій обчислення суми двох чисел цілого і дійсного типів відповідно.
Розділ 9
Багатофайлові програми
9.1 Міжфайлова взаємодія
Проект С++ Builder складається з багатьох файлів, але власне програма дотепер містилася в єдиному файлі Unit1.cpp. Існує багато ситуацій у програмуванні, коли мати лише один файл програми незручно.
Якщо певний проект розробляє група програмістів, кожний з яких виконує окреме завдання, має сенс розв‟язок кожного завдання розташовувати в окремому файлі, а потім долучати всі такі файли до спільного проекту. Такий підхід дозволяє більш оптимально організувати роботу команди програмістів і більш чітко визначити взаємодію частин програми. Відповідно до засад технології програмування, великі програми розбиваються на частини за функціональною направленістю. Один файл може містити програмний код підтримки графічного виведення, другий – математичні розрахунки, третій – код введеннявиведення даних файла. При написанні великих програм тримати всі ці частини в одному файлі-програмі складно чи взагалі неможливо.
Інколи виникає потреба у використовуванні в різних програмах однакових типів і функцій. В таких ситуаціях є сенс винести оголошення цих типів і функцій до окремого файла й долучати його за потреби до різних програм. Має сенс створювати окремі бібліотеки класів і розміщувати їх в окремих файлах.
У багатофайлових програмах елементи програми (змінні, константи, функції тощо), які зберігаються у різних файлах, повинні взаємодіяти один з одним. Розглянемо спочатку окремі cpp-файли. Зазначимо, що область видимості – це частина програми, всередині якої до певної змінної чи іншого елемента програми є доступ (детальніше див. підрозд. 9.5). Приміром, елементи, оголошені всередині функції, мають локальну область видимості, тобто доступ до них існує лише з даної функції. Компоненти класу є видимі лише всередині класу (докладніше про класи див. розд. 14), окрім певних окремих випадків.
Елементи програми, оголошені зовні усіх функцій та класів, мають глобальну область видимості, тобто доступ до них можливий з кожного місця коду програми. Окрім того, глобальні елементи є видимі і з інших файлів проекту.
Уточнимо різницю поміж оголошенням та визначенням змінних у багатофайлових проектах. При оголошенні задаються тип та ім‟я змінної. Це не означає, що для неї одразу ж зарезервується місце у пам‟яті. Оголошення лише повідомляє компіляторові, що десь у програмі може зустрітися змінна такого типу з таким ім‟ям. Змінна визначається, коли для неї зарезервовується місце у пам‟яті, яке здатне містити її значення. Більшість оголошень є визначеннями. Єдиним оголошенням простої змінної, яке не є визначенням, є оголошення з використанням зарезервованого слова extern (без ініціалізації):
int a; |
// Оголошення і визначення |
extern int b; |
// Лише оголошення |
Багатофайлові програми |
303 |
Глобальну змінну може бути визначено у програмі лише одноразово, незалежно від того, скільки файлів міститься у програмі. Локальні змінні з однаковими іменами може бути визначено у різних областях видимості. Але використовувати змінні з однаковими іменами, навіть у різних блоках програми, не рекомендовано, оскільки це може призвести до помилок.
// Файл А
int X; // Визначення змінної Х у файлі А // Файл В
int X; // Так робити не можна: є визначення такої самої змінної Х у файлі А
Але й наступний запис буде неправильним, оскільки у файлі В змінна Х є невідома:
// Файл А
int X; // Визначення змінної Х у файлі А // Файл В
X=3; // Так робити не можна, оскільки змінна Х у файлі В є невідома
Щоб глобальна змінна, визначена в одному файлі, була видимою у всіх файлах програми, слід оголосити її із зарезервованим словом extern:
// Файл А
int X; // Визначення змінної Х у файлі А // Файл В
extern int X; // Визначення змінної Х у файлі B
X=3;
У цьому прикладі оголошення змінної Х у файлі А зробило її видимою у файлі В. Зарезервоване слово extern означає, що оголошення в такому разі – це лише оголошення, й нічого більше. Воно спонукає компілятор, який кожного моменту часу бачить лише один з файлів, не звертати уваги на те, що змінна Х не є визначена у файлі В.
Слід пам‟ятати про одне обмеження: змінну не можна ініціалізувати при оголошенні з extern. Вираз
extern int X=27;
примусить компілятор вважати, що це не є оголошення, а визначення. Тобто він проігнорує слово extern і сприйме оголошення за визначення. Якщо цю змінну в іншому файлі вже визначено, буде видано помилку повторного визначення.
Якщо треба мати дві глобальні змінні з однаковими іменами у двох різних файлах, їх слід визначати із зарезервованим словом static. У такий спосіб область видимості змінної звужується до файла, в якому її визначено. Решта змінних з таким ім‟ям можуть в інших файлах використовуватися без обмежень.
// Файл А |
|
static int X; |
// Визначення: змінна Х є видима лише у файлі А |
// Файл В |
|
static int X; |
// Визначення: змінна Х є видима лише у файлі В |
Хоча тут визначено дві змінні з однаковими іменами, конфлікту це не спричинює. Код, до якого входить звертання до Х, звертатиметься лише до змінної, ви-
Багатофайлові програми |
305 |
|
// Файл А |
|
|
int add(int, int); |
// Оголошення функції (без тіла); |
|
int add(int, int); |
// друге оголошення – помилки немає |
|
Подібно до змінних, функції можна створити невидимими для інших файлів. Для цього при їхньому оголошенні використовується одне й те саме слово static.
// Файл А |
|
static int add(int a, int b) |
// Визначення функції |
{ return a+b; } |
|
// Файл В |
|
static int add(int a, int b) |
// Інша функція |
{ return a+b; } |
|
Цей код створює дві функції, жодна з них не є видима поза файлом, у якому її визначено.
9.2 Заголовні файли
Заголовні файли мають розширення h чи hpp і містять заголовки (прототипи) функцій, а також оголошення типів, констант та змінних з ключовим словом extern. Перевага заголовних файлів полягає у тому, що, коли створено заголовний файл для одного модуля (одного чи декількох cpp-файлів) програми, можна переносити всі зроблені оголошення, які були зроблені, до іншої програми С++. Для цього слід просто залучити заголовний файл за допомогою директиви #include. Зазвичай для кожного cpp-файла створюється власний заголовний файл з таким самим ім‟ям і розширенням h. І, навпаки, здебільшого, для кожного заголовного файла створюється cpp-файл з реалізацією функцій, оголошених у h-файлі.
При залученні до програми заголовного файла його текст (а з ним і текст відповідного cpp-файла) автоматично вставляється до програми замість рядка з відповідною директивою #include. При компіляції програми всі залучені заголовні файли з файлами їхньої реалізації перекомпільовуються, тому залучення надмірної кількості заголовних файлів уповільнює компіляцію програми.
Заголовні файли можна розділити на стандартні й створювані програмістом. Стандартні заголовні файли зберігаються у спеціальній теці INCLUDE, яка є підтекою теки, в яку було встановлено Borland C++. Імена стандартних заголовних файлів пишуться у кутових дужках “<” і “>”, наприклад:
#include <math.h> |
/*Залучення заголовного файла |
|
з математичними функціями */ |
Окрім того, для таких файлів можна не зазначати розширення .h. Якщо стандартний заголовний файл розміщено в іншій теці, слід зазначити його місцезнаходження, наприклад, щоб залучити до програми заголовний файл types.h, який міститься у теці INCLUDE\SYS, слід написати:
#include <include\sys\types.h>
|
Багатофайлові програми |
307 |
Заголовний файл може містити: |
|
|
оголошення типів |
typedef double arr [14]; |
|
шаблони типів |
template <class T> |
|
|
class V { /* ... */ } |
|
оголошення (прототипи) функцій extern int strlen(const char*);
визначення функцій-підстановок inline char get()
{return *p++; }
|
оголошення даних |
extern int a; |
|
визначення констант |
const float pi = 3.141593; |
|
перерахування |
enum bool { false, true }; |
|
оголошення імен |
class Matrix; |
|
команди долучення файлів |
#include <signal.h> |
|
макровизначення |
#define Case break;case |
|
коментарі |
/* Перевірка на кінець файла */ |
Перелік того, що саме слід розміщувати в заголовному файлі, не є вимогою мови С++, це є лише порада розумного використовування долучення файлів. З іншого боку, в заголовному файлі ніколи не повинно бути:
визначення функцій |
char get() { return *p++; } |
визначення даних |
int a; |
визначення складених констант |
const tb[i] = { /* ... */ }; |
Як вже було зазначено, не можна визначити функцію чи то змінну в заголовному файлі, який використовуватиметься кількома файлами програми. Це призводе до помилок повторних визначень. Проблема виникає й тоді, коли помилково залучають один і той самий заголовний файл двічі, наприклад:
// Файл app.cpp з помилкою
#include "headone.h" #include "headone.h"
Припустімо, що є файл з кодом програми app.cpp і два заголовних файли headone.h та headtwo.h. До того ж headone.h долучає до себе headtwo.h. Як-
що є потреба залучити обидва заголовні файли до app.cpp, слід уважно стежити за тим, щоб не залучити один і той самий файл двічі. Наприклад:
//Файл headtwo.h extern int X;
//Файл headone.h
#include "headtwo.h"
//Файл app.cpp
#include "headone.h"
#include "headtwo.h" // Повторне залучення заголовного файла
Оскільки директивою |
#include заголовні файли долучаються до програми |
з усім їхнім умістом, файл app.cpp буде таким: |
|
// Файл app.cpp |
|
. . . |
|
int X; |
// З headtwo.h через headone.h |
. . . |
|
int X; |
// Безпосередньо з headtwo.h |
