Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Вказівники та посилання.docx
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
857.42 Кб
Скачать

6.12. Вказівники на функції

Самі функції мови С++ не є змінними, але можна визначити вказівник на функцію, яку можна обробляти, передавати іншим функціям, розміщати в масиви і т. ін. Розглянемо програму, яка при заданні необов’язкового аргументу –n сортує рядки чисельно, а не лексикографічно.

Алгоритм сортування складається із трьох частин – порівняння (впорядковує будь-які пари об’єктів), перестановки (змінює порядок об’єктів) і сортування (розміщується об’єкт у потрібному порядку). Алгоритм сортування не залежить від операцій порівняння і перестановки, тому передача в нього різних функцій порівняння і перестановки дозволяє організувати сортування за різними критеріями. Саме такий підхід і використано в програмі сортування (див. ПП6.26 на СD).

Лексикографічне порівняння рядків здійснюється функцією Strcmp, а перестановка – функцією Swap. Функція Numcmp порівнює два рядки на базі числового значення і повертає той умовний вказівник, що й Strcmp. Ці три функції описано в Main і вказівники на них передаються у Sort. Тут Strcmp, Nimcmp і Swap – адреси функцій. Передача адрес функцій організовується компілятором. Модифікація функції Sort реалізовано в програмі (див. ПП6.27. на СD)

Опис int (*comp)() означає, що comp є вказівником на функцію, яка повертає значення типу int. Перші круглі дужки тут необхідні, оскільки без них опис int *comp() означав би, що comp є функцією, яка повертає вказівник на цілі значення. Використання comp у рядку if (*comp)(v[j], v[j + gap]) <= 0) повністю відповідає опису, де comp – вказівник на функцію, *comp – сама функція; (*comp)(v[j], v[j + gap]) – звернення до фукції.

Функцію Strcmp, яка порівнює два рядки за першим числовим значенням, реалізовано в програмі ПП6.28. (див. на СD). Функція Swap, яка переставляє два вказівними, реалізована в програмі ПП6.29. (див. на СD).

У C++ існують наступні види вказівників:

1) вказівник на функцію;

2) вказівник на об'єкт;

3) вказівник на void.

Усі ці вказівники відрізняються властивостями і набором припустимих операцій. Вказівник завжди пов'язаний з яким-небудь конкретним типом.

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

Оголошення вказівника на функцію:

тип (*ім'я)(список аргументів);

Приклад

int (*fn)(float,float) – задає вказівник з ім'ям fn на функцію, що повертає значення типу int і має два аргументи типу float.

Приклад програми

#include <iostream.h>

#include <conio.h>

double fx(double x)

{

double y;

y=x*x;

return y;

}

void print_tab(double (*f)(double),float a,float b,float dx)

{

float x;

for (x=a;x<=b;x+=dx)

cout<<x<<'\t'<<f(x)<<endl;

}

void main()

{

clrscr();

cout<<"Таблиця значень функції"<<endl;

print_tab(fx,10,20,0.5);

}

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

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

Вказівник на об'єкт має вид:

тип *ім'я;

При цьому тип може бути будь-яким, крім посилання.

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

Приклад:

int *a,*b; – визначає два вказівники на тип int.

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

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

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

#include <stdio.h>

void AddFive(int Number)

{

Number = Number + 5;

}

void main()

{

int nMyNumber = 18;

printf("Мій старий номер: %d\n", nMyNumber);

AddFive(nMyNumber);

printf("Мій новий номер: %d\n", nMyNumber);

}

Проте тут проблема в тому, що змінна Number, до якої ми звертаємося всередині функції - це копія змінної nMyNumber, що передається у функцію. Таким чином, рядок Number = Number + 5 додає п'ять до копії змінної, залишаючи оригінальну змінну в main() незмінною. Спробуйте запустити програму, щоб переконатися в цьому. Щоб позбавитися від цієї проблеми, ми можемо передавати у функцію вказівник на місце в пам'яті, де зберігається число, але тоді ми повинні поправити функцію, щоб вона приймала вказівник замість числа. Для цього змінимо void AddFive(int Number) на void AddFive(int* Number) додаванням зірочки. Тут знову текст програми, з внесеними змінами. Зверніть увагу, на те що ми повинні переконатися, що передаємо у функцію адресу nMyNumber замість самого числа. Це зроблено за допомогою оператора &, який (як ви пам'ятаєте), читається як "отримати адресу".

#include <stdio.h>

void AddFive(int* Number)

{

*Number = *Number + 5;

}

void main()

{

int nMyNumber = 18;

printf("Мій старий номер: %d\n", nMyNumber);

AddFive(&nMyNumber);

printf("Мій новий номер: %d\n", nMyNumber);

}

Спробуйте придумати ваш власний приклад, щоб продемонструвати це. Звернули увагу на важливе значення * перед Number у AddFive функції? Це необхідно, щоб повідомити компілятору, що ми хочемо збільшити значення змінної, на яку вказує Number, а не значення самого вказівника. Останнє, на що необхідно звернути увагу відносно функцій, це те, що ви можете повернути з функції значення вказівника, наприклад: int * MyFunction(); В цьому прикладі MyFunction повертає вказівник на ціле число Вказівники на класи Є декілька інших застережень з вказівниками, одним з яких є структура чи клас. Ви можете визначити клас наступним чином:

class MyClass

{

public:

int m_Number;

char m_Character;

};

Потім ви можете визначити змінну типу MyClass наступним чином: MyClass thing; Ви повинні це вже знати. Якщо ні, спробуйте прочитати попередню частину. Щоб визначити вказівник на MyClass, ви повинні використовувати: MyClass *thing; ... як ви могли очікувати. При цьому виділяється частина пам’яті і отриманий вказівник буде вказівником на виділену пам'ять: thing = new MyClass; Тут проблема полягає в тому, як же тоді б ви використовували цей вказівник? Ну, як звичайно, ви б написали thing.m_Number, але ви не можете це зробити з вказівником, оскільки thing не є MyClass, а вказівником на нього. Таким чином, thing сама по собі не містить змінну m_Number, вона являє собою структуру, яка вказує на те, що містить m_Number. Тому ми повинні використовувати різні конвенції. Це замінить .(dot) на -> (тире і потім знак більше). Приклад можете побачити нижче:

class MyClass

{

public:

int m_Number;

char m_Character;

};

void main()

{

MyClass *pPointer;

pPointer = new MyClass;

pPointer->m_Number = 10;

pPointer->m_Character = 's';

delete pPointer;

}