Видимість


Непряме використання функцій

Непряме використання функцій: указники на функції, їх тип, ініціалізація і присвоєння

 

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

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);

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