Видимість
Непряме використання функцій
Непряме використання функцій: указники на функції, їх тип, ініціалізація і присвоєння
Існують мови програмування, в яких немає відмінності між програмами і даними. Певною мірою ця можливість представлена в С++ указниками функцій. Так визначення
double (*f) (double);
читається як: нехай f указник на довільну дійсну функцію дійсного аргументу.
Інший запис без дужок
double *f (double);
Від оголошення функції, наприклад,
double sqr (double x);
визначення указника відрізняється тим, що оголошення доповнюється одним визначенням імені функції, наприклад,
double sqr (double x) { return x*x; }
в той час як указник може набувати довільних значень
double *f (double) = 0;
f = sqr ;
f = sin;
бути елементом масиву
double (*g[10]) (double);
Визначення типів дозволяють скорочувати запис
typedef double (*F) (double);
Тепер F повноцінне ім’я типу. Можливі будь-які із наведених нижче варіантів
F f = sqr;
F f = 0;
F f;
f = sqr;
Непрямий виклик функції за її указником записується одним з двох рівносильних способів: f(a) або (*f)(a).
Указники функцій не замінимі при визначенні функціоналів — функцій, параметрами яких служать інші функції
double Newton (double a, double b, double eps,
double (*f) (double))
// або
// double Newton (double a, double b, double eps, F f)
{
while ( (b-a)>eps)
do
{
if (f(a)*f(0.5*(a+b))<0)
// те ж саме: (*f)(a)*(*f)(0.5*(a+b)
b = 0.5*(a+b);
else
a = 0.5*(a+b);
}
return a;
}
Виклики функції запишуться, наприклад, так
x1 = Newton (-1.0, 2.0, 0.00001, sqr);
x2 = Newton (0, pi, 0.00001, cos);
Інший приклад — інтегрування за Сімпсоном
float function Simpson (float a, float b, float eps,
float (*f) (float))
{
int i, n;
float s, ss, s1, s2, s4, h;
n=2; h= (b-a) * 0.5;
s1 = h*(f(a)+f(b));
s2=0;
s4=4*h*f(a+h);
s=s1+s2+s4;
do {
ss = s;
n*=2;
h/=2;
s1*=0.5;
s2=0.5*s2+0.25*s4;
s4=0;
i=1;
do {
s4=s4+f(a+i*h);
i+=2;
}
while (i<=n);
s4=4*h*s4;
s=s1+s2+s4
}
while (abs(s-ss)>eps);
return s/3;
}
4.9. Видимість: глобальна область видимості, локальна область видимості (функція, блок), визначення і оголошення (extern) глобальних об’єктів і функцій, заголовні файли. Розв’язання області видимості
Вхідний текст ділиться на області видимості двох типів: глобальну і локальну. Глобальна область видимості одна для всієї програми, незалежно від того з кількох файлів вона складається. В глобальній області видимості знаходяться об’єкти, визначені поза межами функцій. Локальних областей видимості в програмі багато. Свою локальну область видимості має кожна функція. Всі області видимості функцій не перетинаються. Кожна з них може містити як завгодно багато вкладених один в одного блоків. Кожен блок має власну область видимості.
//глобальна область
const short volume = 256;
int array[volume];
bool binSearch (const int& ax, short size, int value)
// в глобальній області видимі об’єкти:
// volume, array, binSearch
{
// локальна область № 1 — тіло функції
// в ній видимі всі об’єкти, глобальної області
// формальні параметри ax, size, value
// і локальні змінні low і high
int low=0;
int high=size-1;
while (low<high)
{
// локальна область № 2 — тіло циклу
// додається ще одна змінна mid
int mid = (low+high)/2;
if (value<=ax[mid])
high = mid;
else
low = mid +1;
}
// закінчилася область № 2
// mid більше не існує
return (ax[low] == value);
}
// закінчилася область № 1
Може статися, що об’єкти затіняють один одного, глобальний об’єкт можна вивести з тіні оператором розв’язання області дії ::, наприклад,
const int x=1;
int main ()
{
cout<<”глобальний х:”<<x<<endl;
const int x = 2;
// тепер глобальний х більше не видно,
// його затінив локальний об’єкт
cout<<”перший локальний х:”<<x<<endl;
// з тіні він виводиться так ::х
cout<<”знову глобальний х:”<<::x<<endl;
{
const int x=3;
// тепер локальний х більше не видно,
// його затінив інший локальний об’єкт
cout<<”другий локальний х:”<<x<<endl;
};
// знову видимим став перший локальний х
}
Локальна область видимості завжди знаходиться всередині одного файлу, глобальна розподілена маж багатьма файлами. Якщо один і той же глобальний об’єкт вживається у кількох файлах, то для забезпечення роздільної компіляції необхідно подбати про його пояснення в кожному файлі. Нехай перший файл починається так
// Один файл
extern const double pi;
double a = 2*pi;
інший файл може бути схожим
// Інший файл
extern const double pi;
double sqr = pi*pi;
В обох використовується глобальна константа pi. Вона не визначена в них, а лише згадана або, як кажуть оголошена (described). Для виконання програми в ній повинен знайтися (рівно один) файл, в якому оголошений об’єкт буде визначено
// Файл визначення
const double pi =3.14159265358979324;
Функції — це теж глобальні об’єкти, визначення яких ми вже не раз записували, скажімо
int min (int x, int y) { return x<y?x:y;}
В оголошенні функцій слово extern можна не писати
int min (int x, int y);
читається так само, як
extern int min (int x, int y);
Оголошення часто збирають у заголовні файли, для того щоб не повторювати внесені до них тексти їх у кожному файлі. Якщо заголовний файл складаються лише з оголошень, його можна включати у вхідний текст багато разів. Оголошення, але не визначення одного і того ж об’єкта можуть повторюватися. Тому не прийнято розміщувати визначення в заголовних класах. В тих випадках, коли це таки робиться, вживають спеціальних запобіжних заходів, про них пізніше.